[Zope3-checkins] CVS: Zope3/src/zodb - serialize.py:1.2.4.1

Jeremy Hylton jeremy@zope.com
Tue, 21 Jan 2003 11:16:49 -0500


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

Modified Files:
      Tag: new-pickle-branch
	serialize.py 
Log Message:
Change object serialization format.

ZODB pickles objects using a custom format.  Each object pickle had
two parts: the class metadata and the object state.  The class
description must provide enough information to call the class's
``__new__`` and create an empty object.  Once the object exists as a
ghost, its state is passed to ``__setstate__``.

The class metadata is a two-tuple containing the class object and a
tuple of arguments to pass to ``__new__``.  The second element may be
None if the only argument to ``__new__`` is the class.  Since the
first argument is a class, it will normally be pickled as a global
reference.  If the class is itself a persistent object, then the first
part of its instances class metadata will be a persistent reference to
the class.



=== Zope3/src/zodb/serialize.py 1.2 => 1.2.4.1 ===
--- Zope3/src/zodb/serialize.py:1.2	Wed Dec 25 09:12:16 2002
+++ Zope3/src/zodb/serialize.py	Tue Jan 21 11:16:46 2003
@@ -21,31 +21,38 @@
 ghost allows many persistent objects to be loaded while minimizing the
 memory consumption of referenced but otherwise unused objects.
 
-Object introspection
---------------------
-
-XXX Need to define what properties an object must have to be usable
-with the ObjectWriter.  Should document how it determines what the
-class and state of an object are.
-
 Pickle format
 -------------
 
 ZODB pickles objects using a custom format.  Each object pickle had
-two parts: the class description and the object state.  The class
+two parts: the class metadata and the object state.  The class
 description must provide enough information to call the class's
-``__new__`` and create an empty object.  Once the object exists, its
-state is passed to ``__getstate__``.
+``__new__`` and create an empty object.  Once the object exists as a
+ghost, its state is passed to ``__setstate__``.
 
-The class metadata is a three-tuple contained the module name, the
-class name, and a tuple of arguments to pass to ``__new__``.  The last
-element may be None if the only argument to ``__new__`` is the class.
+The class metadata is a two-tuple containing the class object and a
+tuple of arguments to pass to ``__new__``.  The second element may be
+None if the only argument to ``__new__`` is the class.  Since the
+first argument is a class, it will normally be pickled as a global
+reference.  If the class is itself a persistent object, then the first
+part of its instances class metadata will be a persistent reference to
+the class.
 
 Persistent references
 ---------------------
+
 A persistent reference is a pair containing an oid and class metadata.
-XXX Need to write more about when they are used and when plain oids
-are used.
+When one persistent object pickle refers to another persistent object,
+the database uses a persistent reference.  The format allows a
+significant optimization, because ghosts can be created directly from
+persistent references.  If the reference was just an oid, a database
+access would be required to determine the class of the ghost.
+
+Because the persistent reference includes the class, it is not
+possible to change the class of a persistent object.  If a transaction
+changed the class of an object, a new record with new class metadata
+would be written but all the old references would still include the
+old class.
 """
 
 __metaclass__ = type
@@ -55,24 +62,14 @@
 from types import StringType, TupleType
 import logging
 
-def getClass(module, name):
-    mod = __import__(module)
-    parts = module.split(".")
-    for part in parts[1:]:
-        mod = getattr(mod, part)
-    return getattr(mod, name)
-
 def getClassMetadata(obj=None, klass=None):
     if klass is None:
         klass = obj.__class__
-    module = klass.__module__
-    classname = klass.__name__
-    # XXX what if obj is None and we were passed klass?
-    if hasattr(obj, "__getnewargs__"):
-        newargs = obj.__getnewargs__()
-    else:
-        newargs = None
-    return module, classname, newargs
+    # 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
 
 class RootJar:
     def new_oid(self):
@@ -176,8 +173,7 @@
         unpickler.persistent_load = self._persistent_load
         return unpickler
 
-    def _new_object(self, module, classname, newargs=None):
-        klass = getClass(module, classname)
+    def _new_object(self, klass, newargs=None):
         if newargs is None:
             obj = klass.__new__(klass)
         else:
@@ -192,8 +188,8 @@
 
     def getGhost(self, pickle):
         unpickler = self._get_unpickler(pickle)
-        module, classname, newargs = unpickler.load()
-        return self._new_object(module, classname, newargs)
+        klass, newargs = unpickler.load()
+        return self._new_object(klass, newargs)
 
     def getState(self, pickle):
         unpickler = self._get_unpickler(pickle)
@@ -207,8 +203,8 @@
 
     def getObject(self, pickle):
         unpickler = self._get_unpickler(pickle)
-        module, classname, newargs = unpickler.load()
-        obj = self._new_object(module, classname, newargs)
+        klass, newargs = unpickler.load()
+        obj = self._new_object(klass, newargs)
         state = unpickler.load()
         obj.__setstate__(state)
         return obj