[Zope-CVS] CVS: Products/AdaptableStorage/serial - AspectEvent.py:1.3 DeserializationEvent.py:1.6 ObjectSerializer.py:1.5 SerializationEvent.py:1.7

Shane Hathaway shane@zope.com
Fri, 13 Dec 2002 15:42:33 -0500


Update of /cvs-repository/Products/AdaptableStorage/serial
In directory cvs.zope.org:/tmp/cvs-serv32288/serial

Modified Files:
	AspectEvent.py DeserializationEvent.py ObjectSerializer.py 
	SerializationEvent.py 
Log Message:
Hopefully solved the problem of unmanaged persistent objects.

Because AdaptableStorage lets the mappers choose their own persistence
boundaries, some objects that inherit from Persistent may be unknown
to ZODB, even though they are in the database.  I call these unmanaged
persistent objects.

The problem with unmanaged persistent objects surfaces when you change
them without changing their container.  For example, if you add an
item to a BTree, the BTree object (which is managed) does not get
changed, but rather a contained Bucket object (which is often
unmanaged).  We really need the BTree to be notified when the Buckets
change.

To solve this, certain aspect serializers (currently only
RemainderSerializer) now detect unmanaged persistent objects and addq
them to a list.  ZODB looks over this list and assigns them a one-off
"_p_jar" with a register() method.  This special register() method
just sets the _p_changed attribute of the managed object, notifying
ZODB that it must be saved.

I think this is the best solution, even though it's awfully complex to
explain.  The change involved moving the RemainingState aspect to the
"zodb" subpackage, since it already depended on the particulars of the
Persistent base class anyway.  It also required changing
ObjectSerializer so that the event object gets constructed by the
caller, which is appropriate, I think.

Also made some minor changes.


=== Products/AdaptableStorage/serial/AspectEvent.py 1.2 => 1.3 ===
--- Products/AdaptableStorage/serial/AspectEvent.py:1.2	Mon Dec  9 17:11:07 2002
+++ Products/AdaptableStorage/serial/AspectEvent.py	Fri Dec 13 15:42:02 2002
@@ -30,6 +30,7 @@
         MapperEvent.__init__(self, object_mapper, keychain)
         self._keyed_ob_sys = keyed_ob_sys
         self._object = object
+        self._unmanaged = []
 
     def getKeyedObjectSystem(self):
         """Returns the IKeyedObjectSystem that generated this event.
@@ -50,4 +51,13 @@
     def getObject(self):
         """Returns the object being serialized."""
         return self._object
+
+    def addUnmanagedPersistentObjects(self, obs):
+        """Notifies that there are unmanaged persistent objects in the object.
+        """
+        self._unmanaged.extend(obs)
+
+    def getUnmanagedPersistentObjects(self):
+        """Returns the list of unmanaged persistent objects."""
+        return self._unmanaged
 


=== Products/AdaptableStorage/serial/DeserializationEvent.py 1.5 => 1.6 ===
--- Products/AdaptableStorage/serial/DeserializationEvent.py:1.5	Mon Dec  9 17:11:07 2002
+++ Products/AdaptableStorage/serial/DeserializationEvent.py	Fri Dec 13 15:42:02 2002
@@ -35,6 +35,7 @@
 
     def notifyDeserialized(self, name, value):
         """See the IDeserializationEvent interface."""
+        assert self._aspect_name is not None
         self._loaded_refs['%s:%s' % (self._aspect_name, name)] = value
 
     def dereference(self, name, keychain, mapper_names=None):
@@ -48,7 +49,7 @@
 
     # IFullDeserializationEvent interface methods:
 
-    def loadInternalReference(self, ref):
+    def loadInternalRef(self, ref):
         """Returns an object for a reference of the form (aspect_name, name).
         """
         return self._loaded_refs[ref]


=== Products/AdaptableStorage/serial/ObjectSerializer.py 1.4 => 1.5 ===
--- Products/AdaptableStorage/serial/ObjectSerializer.py:1.4	Mon Dec  9 13:25:27 2002
+++ Products/AdaptableStorage/serial/ObjectSerializer.py	Fri Dec 13 15:42:02 2002
@@ -47,21 +47,16 @@
         c = object.__class__
         return (c.__module__ == self._module and c.__name__ == self._name)
 
-    def serialize(self, keyed_ob_sys, object_mapper, keychain, object):
-        event = SerializationEvent(
-            keyed_ob_sys, object_mapper, keychain, object)
+    def serialize(self, object, event):
         full_state = {}
         for name, aspect in self._aspects:
             event.setAspectName(name)
             state = aspect.serialize(object, event)
             if state is not None:
                 full_state[name] = state
-        return full_state, event.getExternalRefs()
+        return full_state
 
-    def deserialize(self, keyed_ob_sys, object_mapper, keychain, object,
-                    full_state):
-        event = DeserializationEvent(
-            keyed_ob_sys, object_mapper, keychain, object)
+    def deserialize(self, object, event, full_state):
         for name, aspect in self._aspects:
             state = full_state.get(name)
             event.setAspectName(name)


=== Products/AdaptableStorage/serial/SerializationEvent.py 1.6 => 1.7 ===
--- Products/AdaptableStorage/serial/SerializationEvent.py:1.6	Mon Dec  9 17:11:07 2002
+++ Products/AdaptableStorage/serial/SerializationEvent.py	Fri Dec 13 15:42:02 2002
@@ -16,13 +16,21 @@
 $Id$
 """
 
-from types import IntType
-
 from interfaces.public import IFullSerializationEvent
 
 from AspectEvent import AspectEvent
 
 
+SIMPLE_IMMUTABLE_OBJECTS = (None, (), 0, 1, '', u'')
+
+try:
+    True
+except NameError:
+    pass
+else:
+    SIMPLE_IMMUTABLE_OBJECTS = SIMPLE_IMMUTABLE_OBJECTS + (False, True)
+
+
 class SerializationEvent (AspectEvent):
 
     __implements__ = IFullSerializationEvent
@@ -48,14 +56,17 @@
     def notifySerialized(self, name, value, is_attribute):
         """See the ISerializationEvent interface."""
         assert self._aspect_name is not None
-        if (value is not None and value != () and not isinstance(
-            value, IntType)):
-            # Make internal references for complex objects only.
+        if value not in SIMPLE_IMMUTABLE_OBJECTS:
+            # Make internal references only for mutable or complex objects.
             idx = id(value)
             if not self._internal_refs.has_key(idx):
                 self._internal_ref_list.append(value)
-                self._internal_refs[idx] = '%s:%s' % (self._aspect_name, name)
-        if is_attribute:
+                if name is not None:
+                    self._internal_refs[idx] = (
+                        '%s:%s' % (self._aspect_name, name))
+                else:
+                    self._internal_refs[idx] = None
+        if is_attribute and name is not None:
             self._attrs[name] = 1
 
     def identifyObject(self, value):
@@ -82,7 +93,6 @@
         """Returns the name of all attributes serialized."""
         return self._attrs.keys()
 
-    def getInternalReference(self, ob):
+    def getInternalRef(self, ob):
         """Returns (aspect_name, name) or None."""
         return self._internal_refs.get(id(ob))
-