add __setstate__ to loads from a serialised object
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 2 Sep 2013 21:04:37 +0000 (23:04 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 2 Sep 2013 21:04:37 +0000 (23:04 +0200)
tiramisu/option.py

index f5b171c..eca61e2 100644 (file)
@@ -98,7 +98,7 @@ class BaseOption(BaseInformation):
     """
     __slots__ = ('_name', '_requires', '_properties', '_readonly',
                  '_consistencies', '_calc_properties', '_state_consistencies',
-                 '_state_requires', '_stated')
+                 '_state_readonly', '_state_requires', '_stated')
 
     def __init__(self, name, doc, requires, properties):
         if not valid_name(name):
@@ -133,7 +133,7 @@ class BaseOption(BaseInformation):
         "frozen" (which has noting to do with the high level "freeze"
         propertie or "read_only" property)
         """
-        if not name.startswith('_state'):
+        if not name.startswith('_state') and name not in ('_cache_paths', '_consistencies'):
             is_readonly = False
             # never change _name
             if name == '_name':
@@ -160,42 +160,58 @@ class BaseOption(BaseInformation):
                                            name))
         object.__setattr__(self, name, value)
 
-    def _impl_convert_consistencies(self, cache):
-        # cache is a dico in import/not a dico in export
-        if self._consistencies is None:
+    def _impl_convert_consistencies(self, descr, load=False):
+        if not load and self._consistencies is None:
             self._state_consistencies = None
+        elif load and self._state_consistencies is None:
+            self._consistencies = None
+            del(self._state_consistencies)
         else:
+            if load:
+                consistencies = self._state_consistencies
+            else:
+                consistencies = self._consistencies
             new_value = []
-            for consistency in self._consistencies:
-                if isinstance(cache, dict):
-                    new_value.append((consistency[0], cache[consistency[1]]))
+            for consistency in consistencies:
+                if load:
+                    new_value.append((consistency[0],
+                                      descr.impl_get_opt_by_path(
+                                          consistency[1])))
                 else:
                     new_value.append((consistency[0],
-                                      cache.impl_get_path_by_opt(
+                                      descr.impl_get_path_by_opt(
                                           consistency[1])))
-            if isinstance(cache, dict):
-                pass
+            if load:
+                del(self._state_consistencies)
+                self._consistencies = tuple(new_value)
             else:
                 self._state_consistencies = tuple(new_value)
 
-    def _impl_convert_requires(self, cache):
-        # cache is a dico in import/not a dico in export
-        if self._requires is None:
+    def _impl_convert_requires(self, descr, load=False):
+        if not load and self._requires is None:
             self._state_requires = None
+        elif load and self._state_requires is None:
+            self._requires = None
+            del(self._state_requires)
         else:
+            if load:
+                _requires = self._state_requires
+            else:
+                _requires = self._requires
             new_value = []
-            for requires in self._requires:
+            for requires in _requires:
                 new_requires = []
                 for require in requires:
-                    if isinstance(cache, dict):
-                        new_require = [cache[require[0]]]
+                    if load:
+                        new_require = [descr.impl_get_opt_by_path(require[0])]
                     else:
-                        new_require = [cache.impl_get_path_by_opt(require[0])]
+                        new_require = [descr.impl_get_path_by_opt(require[0])]
                     new_require.extend(require[1:])
                     new_requires.append(tuple(new_require))
                 new_value.append(tuple(new_requires))
-            if isinstance(cache, dict):
-                pass
+            if load:
+                del(self._state_requires)
+                self._requires = new_value
             else:
                 self._state_requires = new_value
 
@@ -203,8 +219,12 @@ class BaseOption(BaseInformation):
         self._stated = True
         self._impl_convert_consistencies(descr)
         self._impl_convert_requires(descr)
+        try:
+            self._state_readonly = self._readonly
+        except AttributeError:
+            pass
 
-    def __getstate__(self, export=False):
+    def __getstate__(self, stated=True):
         try:
             self._stated
         except AttributeError:
@@ -228,8 +248,24 @@ class BaseOption(BaseInformation):
                         states[slot] = getattr(self, slot)
                     except AttributeError:
                         pass
+        if not stated:
+            del(states['_stated'])
         return states
 
+    def _impl_setstate(self, descr):
+        self._impl_convert_consistencies(descr, load=True)
+        self._impl_convert_requires(descr, load=True)
+        try:
+            self._readonly = self._state_readonly
+            del(self._state_readonly)
+            del(self._stated)
+        except AttributeError:
+            pass
+
+    def __setstate__(self, state):
+        for key, value in state.items():
+            setattr(self, key, value)
+
 
 class Option(BaseOption):
     """
@@ -596,6 +632,11 @@ class SymLinkOption(BaseOption):
         super(SymLinkOption, self)._impl_getstate(descr)
         self._state_opt = descr.impl_get_path_by_opt(self._opt)
 
+    def _impl_setstate(self, descr):
+        self._opt = descr.impl_get_opt_by_path(self._state_opt)
+        del(self._state_opt)
+        super(SymLinkOption, self)._impl_setstate(descr)
+
 
 class IPOption(Option):
     "represents the choice of an ip"
@@ -885,14 +926,16 @@ class OptionDescription(BaseOption):
                          cache_path=None,
                          cache_option=None,
                          _currpath=None,
-                         _consistencies=None):
+                         _consistencies=None,
+                         force_no_consistencies=False):
         if _currpath is None and self._cache_paths is not None:
             # cache already set
             return
         if _currpath is None:
             save = True
             _currpath = []
-            _consistencies = {}
+            if not force_no_consistencies:
+                _consistencies = {}
         else:
             save = False
         if cache_path is None:
@@ -904,10 +947,12 @@ class OptionDescription(BaseOption):
                 raise ConflictError(_('duplicate option: {0}').format(option))
 
             cache_option.append(option)
-            option._readonly = True
+            if not force_no_consistencies:
+                option._readonly = True
             cache_path.append(str('.'.join(_currpath + [attr])))
             if not isinstance(option, OptionDescription):
-                if option._consistencies is not None:
+                if not force_no_consistencies and \
+                        option._consistencies is not None:
                     for consistency in option._consistencies:
                         func, opt = consistency
                         opts = (option, opt)
@@ -920,12 +965,14 @@ class OptionDescription(BaseOption):
                 option.impl_build_cache(cache_path,
                                         cache_option,
                                         _currpath,
-                                        _consistencies)
+                                        _consistencies,
+                                        force_no_consistencies)
                 _currpath.pop()
         if save:
             self._cache_paths = (tuple(cache_option), tuple(cache_path))
-            self._consistencies = _consistencies
-            self._readonly = True
+            if not force_no_consistencies:
+                self._consistencies = _consistencies
+                self._readonly = True
 
     def impl_get_opt_by_path(self, path):
         try:
@@ -1023,15 +1070,38 @@ class OptionDescription(BaseOption):
             option._impl_getstate(descr)
 
     def __getstate__(self):
+        stated = True
         try:
-            del(self._stated)
+            self._stated
         except AttributeError:
             # if cannot delete, _impl_getstate never launch
             # launch it recursivement
             # _stated prevent __getstate__ launch more than one time
             # _stated is delete, if re-serialize, re-lauch _impl_getstate
             self._impl_getstate()
-        return super(OptionDescription, self).__getstate__()
+            stated = False
+        return super(OptionDescription, self).__getstate__(stated)
+
+    def _impl_setstate(self, descr=None):
+        """enables us to import from a dict
+        :param descr: parent :class:`tiramisu.option.OptionDescription`
+        """
+        if descr is None:
+            self._cache_paths = None
+            self.impl_build_cache(force_no_consistencies=True)
+            descr = self
+        self._group_type = getattr(groups, self._state_group_type)
+        del(self._state_group_type)
+        super(OptionDescription, self)._impl_setstate(descr)
+        for option in self.impl_getchildren():
+            option._impl_setstate(descr)
+
+    def __setstate__(self, state):
+        super(OptionDescription, self).__setstate__(state)
+        try:
+            self._stated
+        except AttributeError:
+            self._impl_setstate()
 
 
 def validate_requires_arg(requires, name):