Merge branch 'master' into metaconfig
[tiramisu.git] / tiramisu / config.py
index 4bc0e15..2c1a6b1 100644 (file)
@@ -48,7 +48,7 @@ class SubConfig(object):
         :type subpath: `str` with the path name
         """
         # main option description
-        if not isinstance(descr, OptionDescription):
+        if descr is not None and not isinstance(descr, OptionDescription):
             raise TypeError(_('descr must be an optiondescription, not {0}'
                               ).format(type(descr)))
         self._impl_descr = descr
@@ -169,7 +169,7 @@ class SubConfig(object):
     def cfgimpl_get_description(self):
         if self._impl_descr is None:
             raise ConfigError(_('no option description found for this config'
-                                ' (may be metaconfig without meta)'))
+                                ' (may be GroupConfig)'))
         else:
             return self._impl_descr
 
@@ -477,9 +477,9 @@ class SubConfig(object):
         return context_descr.impl_get_path_by_opt(descr)
 
 
-class CommonConfig(SubConfig):
-    "abstract base class for the Config and the MetaConfig"
-    __slots__ = ('_impl_values', '_impl_settings', '_impl_meta')
+class _CommonConfig(SubConfig):
+    "abstract base class for the Config, GroupConfig and the MetaConfig"
+    __slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
 
     def _impl_build_all_paths(self):
         self.cfgimpl_get_description().impl_build_cache()
@@ -518,7 +518,8 @@ class CommonConfig(SubConfig):
         return None
 
     def cfgimpl_get_meta(self):
-        return self._impl_meta
+        if self._impl_meta is not None:
+            return self._impl_meta()
 
     # information
     def impl_set_information(self, key, value):
@@ -536,42 +537,17 @@ class CommonConfig(SubConfig):
         """
         return self._impl_values.get_information(key, default)
 
-
-# ____________________________________________________________
-class Config(CommonConfig):
-    "main configuration management entry"
-    __slots__ = ('__weakref__', '_impl_test')
-
-    def __init__(self, descr, session_id=None, persistent=False):
-        """ Configuration option management master class
-
-        :param descr: describes the configuration schema
-        :type descr: an instance of ``option.OptionDescription``
-        :param context: the current root config
-        :type context: `Config`
-        :param session_id: session ID is import with persistent Config to
-        retrieve good session
-        :type session_id: `str`
-        :param persistent: if persistent, don't delete storage when leaving
-        :type persistent: `boolean`
-        """
-        settings, values = get_storages(self, session_id, persistent)
-        self._impl_settings = Settings(self, settings)
-        self._impl_values = Values(self, values)
-        super(Config, self).__init__(descr, weakref.ref(self))
-        self._impl_build_all_paths()
-        self._impl_meta = None
-        #undocumented option used only in test script
-        self._impl_test = False
-
+    # ----- state
     def __getstate__(self):
         if self._impl_meta is not None:
-            raise ConfigError('cannot serialize Config with meta')
+            #FIXME _impl_meta est un weakref => faut pas sauvegarder mais faut bien savoir si c'est un méta ou pas au final ...
+            #en fait il faut ne pouvoir sérialisé que depuis une MetaConfig ... et pas directement comme pour les options
+            raise ConfigError('cannot serialize Config with MetaConfig')
         slots = set()
         for subclass in self.__class__.__mro__:
             if subclass is not object:
                 slots.update(subclass.__slots__)
-        slots -= frozenset(['_impl_context', '__weakref__'])
+        slots -= frozenset(['_impl_context', '_impl_meta', '__weakref__'])
         state = {}
         for slot in slots:
             try:
@@ -598,6 +574,35 @@ class Config(CommonConfig):
         storage = get_storage(test=self._impl_test, **state['_storage'])
         self._impl_values._impl_setstate(storage)
         self._impl_settings._impl_setstate(storage)
+        self._impl_meta = None
+
+
+# ____________________________________________________________
+class Config(_CommonConfig):
+    "main configuration management entry"
+    __slots__ = ('__weakref__',)
+
+    def __init__(self, descr, session_id=None, persistent=False):
+        """ Configuration option management master class
+
+        :param descr: describes the configuration schema
+        :type descr: an instance of ``option.OptionDescription``
+        :param context: the current root config
+        :type context: `Config`
+        :param session_id: session ID is import with persistent Config to
+        retrieve good session
+        :type session_id: `str`
+        :param persistent: if persistent, don't delete storage when leaving
+        :type persistent: `boolean`
+        """
+        settings, values = get_storages(self, session_id, persistent)
+        self._impl_settings = Settings(self, settings)
+        self._impl_values = Values(self, values)
+        super(Config, self).__init__(descr, weakref.ref(self))
+        self._impl_build_all_paths()
+        self._impl_meta = None
+        #undocumented option used only in test script
+        self._impl_test = False
 
     def cfgimpl_reset_cache(self,
                             only_expired=False,
@@ -608,99 +613,119 @@ class Config(CommonConfig):
             self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
 
 
-#class MetaConfig(CommonConfig):
-#    __slots__ = ('_impl_children',)
-
-#    def __init__(self, children, meta=True, session_id=None, persistent=False):
-#        if not isinstance(children, list):
-#            raise ValueError(_("metaconfig's children must be a list"))
-#        self._impl_descr = None
-#        self._impl_path = None
-#        if meta:
-#            for child in children:
-#                if not isinstance(child, CommonConfig):
-#                    raise TypeError(_("metaconfig's children "
-#                                      "must be config, not {0}"
-#                                      ).format(type(child)))
-#                if self._impl_descr is None:
-#                    self._impl_descr = child.cfgimpl_get_description()
-#                elif not self._impl_descr is child.cfgimpl_get_description():
-#                    raise ValueError(_('all config in metaconfig must '
-#                                       'have the same optiondescription'))
-#                if child.cfgimpl_get_meta() is not None:
-#                    raise ValueError(_("child has already a metaconfig's"))
-#                child._impl_meta = self
-
-#        self._impl_children = children
-#        settings, values = get_storages(self, session_id, persistent)
-#        self._impl_settings = Settings(self, settings)
-#        self._impl_values = Values(self, values)
-#        self._impl_meta = None
-
-#    def cfgimpl_get_children(self):
-#        return self._impl_children
-
-#    def cfgimpl_get_context(self):
-#        "a meta config is a config wich has a setting, that is itself"
-#        return self
-
-#    def cfgimpl_reset_cache(self,
-#                            only_expired=False,
-#                            only=('values', 'settings')):
-#        if 'values' in only:
-#            self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
-#        if 'settings' in only:
-#            self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
-#        for child in self._impl_children:
-#            child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
-
-#    def set_contexts(self, path, value):
-#        for child in self._impl_children:
-#            try:
-#                if not isinstance(child, MetaConfig):
-#                    setattr(child, path, value)
-#                else:
-#                    child.set_contexts(path, value)
-#            except PropertiesOptionError:
-#                pass
-
-#    def find_first_contexts(self, byname=None, bypath=None, byvalue=None,
-#                            type_='path', display_error=True):
-#        ret = []
-#        try:
-#            if bypath is None and byname is not None and \
-#                    self.cfgimpl_get_description() is not None:
-#                bypath = self._find(bytype=None, byvalue=None, byname=byname,
-#                                    first=True, type_='path',
-#                                    check_properties=False,
-#                                    display_error=display_error)
-#        except ConfigError:
-#            pass
-#        for child in self._impl_children:
-#            try:
-#                if not isinstance(child, MetaConfig):
-#                    if bypath is not None:
-#                        if byvalue is not None:
-#                            if getattr(child, bypath) == byvalue:
-#                                ret.append(child)
-#                        else:
-#                            #not raise
-#                            getattr(child, bypath)
-#                            ret.append(child)
-#                    else:
-#                        ret.append(child.find_first(byname=byname,
-#                                                    byvalue=byvalue,
-#                                                    type_=type_,
-#                                                    display_error=False))
-#                else:
-#                    ret.extend(child.find_first_contexts(byname=byname,
-#                                                         bypath=bypath,
-#                                                         byvalue=byvalue,
-#                                                         type_=type_,
-#                                                         display_error=False))
-#            except AttributeError:
-#                pass
-#        return self._find_return_results(ret, display_error)
+class GroupConfig(_CommonConfig):
+    __slots__ = ('_impl_children', '__weakref__')
+
+    def __init__(self, children, session_id=None, persistent=False,
+                 _descr=None):
+        if not isinstance(children, list):
+            raise ValueError(_("metaconfig's children must be a list"))
+        self._impl_children = children
+        settings, values = get_storages(self, session_id, persistent)
+        self._impl_settings = Settings(self, settings)
+        self._impl_values = Values(self, values)
+        super(GroupConfig, self).__init__(_descr, weakref.ref(self))
+        self._impl_meta = None
+        #undocumented option used only in test script
+        self._impl_test = False
+
+    def cfgimpl_get_children(self):
+        return self._impl_children
+
+    #def cfgimpl_get_context(self):
+    #    "a meta config is a config which has a setting, that is itself"
+    #    return self
+    #
+    def cfgimpl_reset_cache(self,
+                            only_expired=False,
+                            only=('values', 'settings')):
+        if 'values' in only:
+            self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
+        if 'settings' in only:
+            self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
+        for child in self._impl_children:
+            child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
+
+    def setattrs(self, path, value):
+        """Setattr not in current GroupConfig, but in each children
+        """
+        for child in self._impl_children:
+            try:
+                if not isinstance(child, GroupConfig):
+                    setattr(child, path, value)
+                else:
+                    child.setattrs(path, value)
+            except PropertiesOptionError:
+                pass
+
+    def find_firsts(self, byname=None, bypath=None, byvalue=None,
+                    type_='path', display_error=True):
+        """Find first not in current GroupConfig, but in each children
+        """
+        ret = []
+        #if MetaConfig, all children have same OptionDescription as context
+        #so search only one time for all children
+        try:
+            if bypath is None and byname is not None and \
+                    isinstance(self, MetaConfig):
+                bypath = self._find(bytype=None, byvalue=None, byname=byname,
+                                    first=True, type_='path',
+                                    check_properties=False,
+                                    display_error=display_error)
+                byname = None
+        except AttributeError:
+            pass
+        for child in self._impl_children:
+            try:
+                if not isinstance(child, MetaConfig):
+                    if bypath is not None:
+                        #if byvalue is None, try if not raise
+                        value = getattr(child, bypath)
+                        if byvalue is not None:
+                            if isinstance(value, Multi):
+                                if byvalue in value:
+                                    ret.append(child)
+                            else:
+                                if value == byvalue:
+                                    ret.append(child)
+                        else:
+                            ret.append(child)
+                    else:
+                        ret.append(child.find_first(byname=byname,
+                                                    byvalue=byvalue,
+                                                    type_=type_,
+                                                    display_error=False))
+                else:
+                    ret.extend(child.find_firsts(byname=byname,
+                                                 bypath=bypath,
+                                                 byvalue=byvalue,
+                                                 type_=type_,
+                                                 display_error=False))
+            except AttributeError:
+                pass
+        return self._find_return_results(ret, display_error)
+
+
+class MetaConfig(GroupConfig):
+    __slots__ = tuple()
+
+    def __init__(self, children, session_id=None, persistent=False):
+        descr = None
+        for child in children:
+            if not isinstance(child, _CommonConfig):
+                raise TypeError(_("metaconfig's children "
+                                  "should be config, not {0}"
+                                  ).format(type(child)))
+            if child.cfgimpl_get_meta() is not None:
+                raise ValueError(_("child has already a metaconfig's"))
+            if descr is None:
+                descr = child.cfgimpl_get_description()
+            elif not descr is child.cfgimpl_get_description():
+                raise ValueError(_('all config in metaconfig must '
+                                   'have the same optiondescription'))
+            child._impl_meta = weakref.ref(self)
+
+        super(MetaConfig, self).__init__(children, session_id, persistent, descr)
 
 
 def mandatory_warnings(config):