[Zodb-checkins] CVS: StandaloneZODB/ZODB - PersistentMapping.py:1.13.2.3

Jeremy Hylton jeremy@zope.com
Fri, 30 Nov 2001 13:26:01 -0500


Update of /cvs-repository/StandaloneZODB/ZODB
In directory cvs.zope.org:/tmp/cvs-serv6306

Modified Files:
      Tag: StandaloneZODB-1_0-branch
	PersistentMapping.py 
Log Message:
Merge in new __getstate__() and __setstate__() from the trunk.


=== StandaloneZODB/ZODB/PersistentMapping.py 1.13.2.2 => 1.13.2.3 ===
     changes are registered.  As a side effect, mapping objects may be
     subclassed.
+
+    A subclass of PersistentMapping or any code that adds new
+    attributes should not create an attribute named _container.  This
+    is reserved for backwards compatibility reasons.
     """
 
+    # UserDict provides all of the mapping behavior.  The
+    # PersistentMapping class is responsible marking the persistent
+    # state as changed when a method actually changes the state.  At
+    # the mapping API evolves, we may need to add more methods here.
+
     __super_delitem = UserDict.__delitem__
     __super_setitem = UserDict.__setitem__
     __super_clear = UserDict.clear
@@ -31,21 +40,6 @@
     __super_setdefault = UserDict.setdefault
     __super_popitem = UserDict.popitem
 
-    def __setstate__(self, state):
-        # The old PersistentMapping used _container to hold the data.
-        # We need to make the current code work with objects pickled
-        # using the old code.  Unfortunately, this forces us to expose
-        # the rep of UserDict, because __init__() won't be called when
-        # a pickled object is being loaded.
-        if state.has_key('_container'):
-            assert not state.has_key('data'), \
-                   ("object state has _container and data attributes: %s"
-                    % repr(state))
-            self.data = state['_container']
-            del state['_container']
-        for k, v in state.items():
-            self.__dict__[k] = v
-
     def __delitem__(self, key):
         self.__super_delitem(key)
         self._p_changed = 1
@@ -73,3 +67,31 @@
     def popitem(self):
         self._p_changed = 1
         return self.__super_popitem()
+
+    # If the internal representation of PersistentMapping changes,
+    # it causes compatibility problems for pickles generated by
+    # different versions of the code.  Compatibility works in both
+    # directions, because an application may want to share a database
+    # between applications using different versions of the code.
+    
+    # Effectively, the original rep is part of the "API."  To provide
+    # full compatibility, the getstate and setstate must read and
+    # right objects using the old rep.
+
+    # As a result, the PersistentMapping must save and restore the
+    # actual internal dictionary using the name _container.
+
+    def __getstate__(self):
+        state = {}
+        state.update(self.__dict__)
+        state['_container'] = state['data']
+        del state['data']
+        return state
+
+    def __setstate__(self, state):
+        if state.has_key('_container'):
+            self.data = state['_container']
+            del state['_container']
+        elif not state.has_key('data'):
+            self.data = {}
+        self.__dict__.update(state)