[Zodb-checkins] CVS: Zope3/src/zodb - serialize.py:1.10 conflict.py:1.8

Jeremy Hylton jeremy@zope.com
Fri, 7 Mar 2003 18:09:53 -0500


Update of /cvs-repository/Zope3/src/zodb
In directory cvs.zope.org:/tmp/cvs-serv11711/zodb

Modified Files:
	serialize.py conflict.py 
Log Message:
Simplify getClassMetadata() and uncover a small tarpit.

The getClassMetadata() was using hasattr() and that was swallowing a
lot of exceptions.  (Reminder: Never use hasattr() :-).  The problem,
described in comments in the code, is that getClassMetadata() is
called on ghost objects where we can't check for a __getnewargs__().

Something needs to be done about this, but put a band-aid on it for
now.


=== Zope3/src/zodb/serialize.py 1.9 => 1.10 ===
--- Zope3/src/zodb/serialize.py:1.9	Thu Mar  6 15:34:25 2003
+++ Zope3/src/zodb/serialize.py	Fri Mar  7 18:09:51 2003
@@ -62,14 +62,36 @@
 from types import StringType, TupleType
 import logging
 
-def getClassMetadata(obj=None, klass=None):
-    if klass is None:
-        klass = obj.__class__
-    # XXX Not sure I understand the obj==None casse
-    newargs = None
-    if obj is not None and hasattr(obj, "__getnewargs__"):
-            newargs = obj.__getnewargs__()
-    return klass, newargs
+def getClassMetadata(obj):
+    """Return 2-tuple that identifies class for ghost creation.
+
+    The 2-tuple contains the class of obj and a sequence of
+    arguments to pass to the class's __new__() to create a new
+    instance.  If __new__() takes no extra arguments, the second
+    element of the tuple is None.
+    """
+    # XXX This is a hack.  If the object is in a ghost state,
+    # we can't know whether its has a __getnewargs__ without
+    # loading the object -- but loading the object could fail
+    # with a ReadConflictError.  Perhaps this can go away
+    # when MVCC is here.
+
+    # It happens to be the case that the only class I know of
+    # that defines a __getnewargs__() does not allow its instances
+    # to be ghosts.  Maybe that is sufficient.
+
+    # Another alternative is to not store the class metadata
+    # with the object for references where we can't determine
+    # the full metadata.  These objects would require a database
+    # load in order to create ghosts.
+    
+    if obj._p_state == 3:
+        newargs = None
+    else:
+        newargs = getattr(obj, "__getnewargs__", None)
+        if newargs is not None:
+            newargs = newargs()
+    return obj.__class__, newargs
 
 class RootJar:
     def newObjectId(self):
@@ -115,7 +137,7 @@
         # -- but I can't because the type doesn't have a canonical name.
         # Instead, we'll assert that an oid must always be a string
         if not (oid is None or isinstance(oid, StringType)):
-            # XXX log a warning
+            logging.warn("unexpected _p_oid: %r", oid)
             return None
 
         if oid is None or obj._p_jar is not self._jar:


=== Zope3/src/zodb/conflict.py 1.7 => 1.8 ===
--- Zope3/src/zodb/conflict.py:1.7	Tue Feb 25 13:55:02 2003
+++ Zope3/src/zodb/conflict.py	Fri Mar  7 18:09:51 2003
@@ -23,7 +23,7 @@
 import logging
 
 from zodb.interfaces import ConflictError
-from zodb.serialize import BaseObjectReader, ObjectWriter, getClassMetadata
+from zodb.serialize import BaseObjectReader, ObjectWriter
 
 ResolvedSerial = "rs"
 
@@ -43,6 +43,7 @@
     def __init__(self, ghost, state):
         self._class = ghost.__class__
         self._state = state
+        self._p_state = 0 # required to make getClassMetadata() happy
 
     def __getattribute__(self, name):
         if name == "__class__":
@@ -97,8 +98,10 @@
         # In a ZEO environment, this method isn't as useful.  If the
         # method is called from a client, it will always return False,
         # because the conflict resolution code runs on the server.
-        meta = getClassMetadata(klass=klass)
-        return meta in cls.bad_classes
+
+        # This method depends on the representation of class metadata
+        # that is otherwise only used inside zodb.serialize.
+        return (klass, None) in cls.bad_classes
 
     unresolvable = classmethod(unresolvable)