[Zodb-checkins] CVS: Zope3/src/zodb/code - patch.py:1.7 class_.py:1.10

Jeremy Hylton jeremy@zope.com
Thu, 30 Jan 2003 13:50:18 -0500


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

Modified Files:
	patch.py class_.py 
Log Message:
A collection of small improvements.

Set _pc_init to False inside constructor, although I'm not entirely
certain why it is necessary.  Sometimes a class would get loaded
through a new connection or invalidated and its _pc_init would be
True.

Remove unneeded __call__().  A class is never a ghost.

Add comment and commented-out test of the need for _p_changed.

First steps towards a more useful __getstate__().

Remove stale comment in patch.TypeWrapper.

Re-enable testing of _p_oid attribute.




=== Zope3/src/zodb/code/patch.py 1.6 => 1.7 ===
--- Zope3/src/zodb/code/patch.py:1.6	Tue Jan 28 14:23:42 2003
+++ Zope3/src/zodb/code/patch.py	Thu Jan 30 13:50:15 2003
@@ -132,24 +132,7 @@
 class TypeWrapper(Wrapper):
 
     def unwrap(self, bases, dict):
-        # XXX Add Persistent to the list of bases so that type (the
-        # base class of PersistentClassMetaClass) will create the
-        # correct C layout.
-
-        # We must maintain a linearizable MRO when adding Persistent
-        # to list of bases.  In particular, object is in Persistent's
-        # __bases__ to Persistent must occur before object in the
-        # new class's __bases__.
-
-        if not Persistent in bases:
-            if object in bases:
-                L = list(bases)
-                i = L.index(object)
-                newbases = bases[:i] + (Persistent,) + bases[i:]
-            else:
-                newbases = bases + (Persistent,)
-
-        return PersistentClassMetaClass(self._obj.__name__, newbases, dict)
+        return PersistentClassMetaClass(self._obj.__name__, bases, dict)
 
 def registerWrapper(atype, wrapper, unwrap_thunk):
     """Register a patch wrapper for an external object type."""
@@ -194,6 +177,10 @@
             # They are classes, but can't be pickled as globals because
             # pickle looks in sys.modules and the persistent import
             # doesn't use sys.modules.
+
+            # If we find a class, pickle it via save_type()
+            if isinstance(obj, PersistentClassMetaClass):
+                return None
             
             # XXX Is this safe in all cases?
             oid = getattr(obj, "_p_oid", marker)
@@ -213,10 +200,14 @@
             self.write(self.put(memo_len))
             self.memo[id(atype)] = memo_len, None
         else:
-            self.save_global(atype)
+            if isinstance(atype, PersistentClassMetaClass):
+                self.save_pers(self.persistent_id(atype, True))
+            else:
+                self.save_global(atype)
 
     dispatch[TypeType] = save_type
     dispatch[ClassType] = save_type
+    dispatch[PersistentClassMetaClass] = save_type
 
     def save_function(self, func):
         if pickle.whichmodule(func, func.__name__) == "__main__":


=== Zope3/src/zodb/code/class_.py 1.9 => 1.10 ===
--- Zope3/src/zodb/code/class_.py:1.9	Tue Jan 28 17:03:43 2003
+++ Zope3/src/zodb/code/class_.py	Thu Jan 30 13:50:15 2003
@@ -197,8 +197,6 @@
                                     self._func)
 
 
-# XXX is missing necessary for findattr?
-# None might be sufficient
 _missing = object()
 
 def findattr(cls, attr, default):
@@ -209,6 +207,10 @@
             return o
     return default
 
+class StateChangeDataDescr(ExtClassDataDescr):
+    # A data descriptor for _p_changed.
+    pass
+
 class PersistentClassMetaClass(PersistentMetaClass):
 
     # An attempt to make persistent classes look just like other
@@ -231,8 +233,6 @@
 
     __implements__ = IPersistent
 
-    _pc_init = False
-
     # A class is normally created in the UPTODATE state, but when a
     # new ghost is created for it the serialization machinery passes
     # GHOST instead of UPTODATE.  See __getnewargs__().
@@ -240,6 +240,7 @@
     def __new__(meta, name, bases, dict, state=UPTODATE):
         cls = super(PersistentClassMetaClass, meta).__new__(
             meta, name, bases, dict)
+        cls._pc_init = False
 
         # helper functions
         def extend_attr(attr, v):
@@ -253,9 +254,6 @@
         extend_attr("_p_oid", None)
         extend_attr("_p_atime", time.time() % 86400)
         extend_attr("_p_state", state)
-        # XXX A persistent class needs a proprety for _p_changed
-        # so that it can be used to register with the transaction
-        # manager.
         extend_attr("_p_changed", None)
         extend_meth("_p_activate", meta._p_activate)
         extend_meth("_p_deactivate", meta._p_activate)
@@ -280,13 +278,6 @@
         cls._pc_init = True
         return cls
 
-    def __call__(cls, *args, **kwargs):
-        # type's __call__() uses tp_init directly, so it bypasses our
-        # __getattribute__() hook.  So we must guarantee that a class
-        # is loaded before it is called.
-        cls._p_activate()
-        return super(PersistentClassMetaClass, cls).__call__(*args, **kwargs)
-
     def __getattribute__(cls, name):
         # XXX I'm not sure I understand this code any more.
         super_meth = super(PersistentClassMetaClass, cls).__getattribute__
@@ -303,6 +294,9 @@
                 cls._p_atime = int(time.time() % 86400)
         return super_meth(name)
 
+    # XXX There needs to be an _p_changed flag so that classes get
+    # registered with the txn when they are modified.
+
     def __setattr__(cls, attr, val):
         if not attr.startswith("_pc_") and cls._pc_init:
             descr = cls.__dict__.get(attr)
@@ -310,42 +304,51 @@
                 set = getattr(descr, "__set__", None)
                 if set is not None:
                     set(None, val)
+##                    cls._p_changed = True
                     return
         super(PersistentClassMetaClass, cls).__setattr__(attr, val)
 
     def __delattr__(cls, attr):
         if attr.startswith('_p_'):
-            if attr == "_p_changed":
-                # this means something special
-                pass
-            else:
-                return
+            # XXX what should happen with these?
+            return
         super(PersistentClassMetaClass, cls).__delattr__(attr)
 
     def __repr__(cls):
         return "<persistent class %s.%s>" % (cls.__module__,
                                              cls.__name__)
 
-    # XXX This stuff only handles methods, not data members, but it's
-    # a start.
-
-    # The state of a persistent class is its persistent functions.
-    # They need to be extracted from the methods when the class is pickled
-    # and put back into descriptors when they are unpickled.
-
-    # How to recognize other class attributes that need to be pickled?
+    # It should be possible for getstate / setstate to deal with
+    # arbitrary class attributes.  That goal is hard to achieve,
+    # because there are several funny descriptors that need to
+    # be handled specially.
 
     def __getstate__(cls):
         dict = {}
+        
         for k in cls.__dict__.keys():
             v = getattr(cls, k)
             if isinstance(v, PersistentMethod):
                 dict[k] = v.im_func
+                continue
+            if (k in ["__module__", "__weakref__", "__dict__"]
+                or k.startswith("_p_") or k.startswith("_pc_")):
+                continue
+            # XXX The following test isn't right because overriding
+            # must be allowed, but I haven't figured that out yet.
+            # __getstate__ and __setstate__ might be overridden
+            # __implements__ might be overridden
+            if k in ["__getstate__", "__setstate__", "__implements__"]:
+                continue
+            dict[k] = v
         return dict
 
     def __setstate__(cls, dict):
         for k, v in dict.items():
-            setattr(cls, k, PersistentDescriptor(cls, v))
+            if isinstance(v, PersistentFunction):
+                setattr(cls, k, PersistentDescriptor(cls, v))
+            else:
+                setattr(cls, k, v)
 
     # XXX Should the object get marked as a ghost when it is, in fact,
     # not a ghost?  The most obvious answer is no.  But if we don't
@@ -376,7 +379,7 @@
                     # XXX Should really put in special inconsistent state
                     cls._p_state = UPTODATE
             else:
-                pass # XXX should log here
+                print id(cls), "dm", dm, "oid", cls._p_oid
 
     # Methods below here are not wrapped to be class-only attributes.
     # They are available as methods of classes using this metaclass.