[Zodb-checkins] CVS: Zope3/src/zodb/code - class_.py:1.5

Jeremy Hylton jeremy@zope.com
Mon, 30 Dec 2002 19:18:45 -0500


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

Modified Files:
	class_.py 
Log Message:
First attempt at repairing pickling for persistent classes.


=== Zope3/src/zodb/code/class_.py 1.4 => 1.5 ===
--- Zope3/src/zodb/code/class_.py:1.4	Mon Dec 30 18:05:02 2002
+++ Zope3/src/zodb/code/class_.py	Mon Dec 30 19:18:44 2002
@@ -182,8 +182,8 @@
         self.__setstate__ = func.__setstate__
 
     def __repr__(self):
-        return "<descriptor %s.%s>" % (self.__objclass__.__name__,
-                                       self.__name__)
+        return "<persistent descriptor %s.%s>" % (self.__objclass__.__name__,
+                                                  self.__name__)
 
     def __get__(self, object, klass=None):
         if object is None:
@@ -219,7 +219,11 @@
 
     _pc_init = False
 
-    def __new__(meta, name, bases, dict):
+    # 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__().
+
+    def __new__(meta, name, bases, dict, state=UPTODATE):
         cls = super(PersistentClassMetaClass, meta).__new__(
             meta, name, bases, dict)
         # helper functions
@@ -234,7 +238,7 @@
         extend_attr("_p_oid", None)
         extend_attr("_p_jar", None)
         extend_attr("_p_atime", time.time() % 86400)
-        extend_attr("_p_state", UPTODATE)
+        extend_attr("_p_state", state)
         extend_meth("_p_activate", meta._p_activate)
         extend_meth("_p_deactivate", meta._p_activate)
         extend_meth("__getstate__", meta.__getstate__)
@@ -248,11 +252,6 @@
         cls._pc_init = True
         return cls
 
-    def fixup(cls, mod):
-        for k, v in cls.__dict__.items():
-            if isinstance(v, function):
-                setattr(cls, k, PersistentFunction(v, mod))
-
     def __getattribute__(cls, name):
         # XXX I'm not sure I understand this code any more.
         super_meth = super(PersistentClassMetaClass, cls).__getattribute__
@@ -292,33 +291,54 @@
         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?
+
     def __getstate__(cls):
         dict = {}
-        for k, v in cls.__dict__.items():
-            if hasattr(v, '_p_oid'):
-                dict[k] = v
+        for k in cls.__dict__.keys():
+            v = getattr(cls, k)
+            if isinstance(v, PersistentMethod):
+                dict[k] = v.im_func
         return dict
 
     def __setstate__(cls, dict):
         for k, v in dict.items():
-            setattr(cls, k, v)
+            setattr(cls, k, PersistentDescriptor(cls, v))
 
     def _p_deactivate(cls):
         # do nothing but mark the state change for now
         cls._p_state = GHOST
 
     def _p_activate(cls):
-        if cls._p_state is None:
+        if cls._p_state == GHOST:
             dm = cls._p_jar
             if dm is not None:
-                # reactivate
-                cls._p_state = UPTODATE
+                cls._p_state = CHANGED
+                try:
+                    dm.setstate(cls)
+                finally:
+                    # XXX Should really put in special inconsistent state
+                    cls._p_state = UPTODATE
 
     # Methods below here are not wrapped to be class-only attributes.
     # They are available as methods of classes using this metaclass.
 
     def __getnewargs__(cls):
-        return cls.__name__, cls.__bases__, {}
+        # XXX This should really be _p_getnewargs() or something like that.
+        
+        # If the class is later loaded and unghostified, the arguments
+        # passed to __new__() won't have an __module__.  It seems that
+        # the module gets set to zodb.code.class_ in that case, which
+        # is wrong.
+        return (cls.__name__, cls.__bases__,
+                {"__module__": cls.__module__}, GHOST)
 
     def _p_newstate(cls, acls):
         # Update a class's __dict__ in place.  Must use setattr and