[Zope-Checkins] CVS: Zope/lib/python/ZODB - serialize.py:1.2 BaseStorage.py:1.37 ConflictResolution.py:1.21 Connection.py:1.102 DB.py:1.57 DemoStorage.py:1.22 ExportImport.py:1.18 FileStorage.py:1.140 MappingStorage.py:1.10 Mount.py:1.21 POSException.py:1.22 Transaction.py:1.55 __init__.py:1.26 conversionhack.py:1.5 coptimizations.c:1.24 fsIndex.py:1.5 fsdump.py:1.14 fspack.py:1.14 fsrecover.py:1.14 fstools.py:1.2 utils.py:1.19 PersistentList.py:NONE PersistentMapping.py:NONE cPersistence.c:NONE cPersistence.h:NONE

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:45:24 EST 2003


Update of /cvs-repository/Zope/lib/python/ZODB
In directory cvs.zope.org:/tmp/cvs-serv3783/lib/python/ZODB

Modified Files:
	BaseStorage.py ConflictResolution.py Connection.py DB.py 
	DemoStorage.py ExportImport.py FileStorage.py 
	MappingStorage.py Mount.py POSException.py Transaction.py 
	__init__.py conversionhack.py coptimizations.c fsIndex.py 
	fsdump.py fspack.py fsrecover.py fstools.py utils.py 
Added Files:
	serialize.py 
Removed Files:
	PersistentList.py PersistentMapping.py cPersistence.c 
	cPersistence.h 
Log Message:
Merged Jeremy and Tim's changes from the zodb33-devel-branch.


=== Zope/lib/python/ZODB/serialize.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:24 2003
+++ Zope/lib/python/ZODB/serialize.py	Fri Nov 28 11:44:49 2003
@@ -0,0 +1,273 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Support for ZODB object serialization.
+
+ZODB serializes objects using a custom format based on Python pickles.
+When an object is unserialized, it can be loaded as either a ghost or
+a real object.  A ghost is a persistent object of the appropriate type
+but without any state.  The first time a ghost is accessed, the
+persistence machinery traps access and loads the actual state.  A
+ghost allows many persistent objects to be loaded while minimizing the
+memory consumption of referenced but otherwise unused objects.
+
+Pickle format
+-------------
+
+ZODB stores serialized objects using a custom format based on pickle.
+Each serialized object has 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 can be represented in two different ways, in order
+to provide backwards compatibility with many earlier versions of ZODB.
+The class metadata is always a two-tuple.  The first element may also
+be a tuple, containing two string elements: name of a module and the
+name of a class.  The second element of the class metadata tuple is a
+tuple of arguments to pass to the class's ``__new__``.
+
+Persistent references
+---------------------
+
+A persistent reference is a pair containing an oid and class metadata.
+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.
+
+"""
+
+import cPickle
+import cStringIO
+
+from ZODB.coptimizations import new_persistent_id
+
+_marker = object()
+
+def myhasattr(obj, attr):
+    """Returns True or False or raises an exception."""
+    val = getattr(obj, attr, _marker)
+    return val is not _marker
+
+def getClassMetadata(obj):
+    klass = obj.__class__
+    if issubclass(klass, type):
+        # Handle ZClasses.
+        d = obj.__dict__.copy()
+        del d["_p_jar"]
+        args = obj.__name__, obj.__bases__, d
+        return klass, args
+    else:
+        getinitargs = getattr(klass, "__getinitargs__", None)
+        if getinitargs is None:
+            args = None
+        else:
+            args = getinitargs()
+        mod = getattr(klass, "__module__", None)
+        if mod is None:
+            return klass, args
+        else:
+            return (mod, klass.__name__), args
+
+class BaseObjectWriter:
+    """Serializes objects for storage in the database.
+
+    The ObjectWriter creates object pickles in the ZODB format.  It
+    also detects new persistent objects reachable from the current
+    object.
+
+    The client is responsible for calling the close() method to avoid
+    leaking memory.  The ObjectWriter uses a Pickler internally, and
+    Pickler objects do not participate in garbage collection.  (Note
+    that in Python 2.3 and higher, the close() method would be
+    unnecessary because Picklers participate in garbage collection.)
+    """
+
+    def __init__(self, jar=None):
+        self._file = cStringIO.StringIO()
+        self._p = cPickle.Pickler(self._file, 1)
+        self._stack = []
+        self._p.persistent_id = new_persistent_id(jar, self._stack)
+        if jar is not None:
+            assert myhasattr(jar, "new_oid")
+        self._jar = jar
+
+    def serialize(self, obj):
+        return self._dump(getClassMetadata(obj), obj.__getstate__())
+
+    def _dump(self, classmeta, state):
+        # To reuse the existing cStringIO object, we must reset
+        # the file position to 0 and truncate the file after the
+        # new pickle is written.
+        self._file.seek(0)
+        self._p.clear_memo()
+        self._p.dump(classmeta)
+        self._p.dump(state)
+        self._file.truncate()
+        return self._file.getvalue()
+
+class ObjectWriter(BaseObjectWriter):
+
+    def __init__(self, obj):
+        BaseObjectWriter.__init__(self, obj._p_jar)
+        self._stack.append(obj)
+
+    def __iter__(self):
+        return NewObjectIterator(self._stack)
+
+class NewObjectIterator:
+
+    # The pickler is used as a forward iterator when the connection
+    # is looking for new objects to pickle.
+
+    def __init__(self, stack):
+        self._stack = stack
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        if self._stack:
+            elt = self._stack.pop()
+            return elt
+        else:
+            raise StopIteration
+
+class BaseObjectReader:
+
+    def _persistent_load(self, oid):
+        # subclasses must define _persistent_load().
+        raise NotImplementedError
+
+    def _get_class(self, module, name):
+        # subclasses must define _get_class()
+        raise NotImplementedError
+
+    def _get_unpickler(self, pickle):
+        file = cStringIO.StringIO(pickle)
+        unpickler = cPickle.Unpickler(file)
+        unpickler.persistent_load = self._persistent_load
+        return unpickler
+
+    def _new_object(self, klass, args):
+        if not args and not myhasattr(klass, "__getinitargs__"):
+            obj = klass.__new__(klass)
+        else:
+            obj = klass(*args)
+            if not isinstance(klass, type):
+                obj.__dict__.clear()
+
+        return obj
+
+    def getClassName(self, pickle):
+        unpickler = self._get_unpickler(pickle)
+        klass, newargs = unpickler.load()
+        if isinstance(klass, tuple):
+            return "%s.%s" % klass
+        else:
+            return klass.__name__
+
+    def getGhost(self, pickle):
+        unpickler = self._get_unpickler(pickle)
+        klass, args = unpickler.load()
+        if isinstance(klass, tuple):
+            klass = self._get_class(*klass)
+
+        return self._new_object(klass, args)
+
+    def getState(self, pickle):
+        unpickler = self._get_unpickler(pickle)
+        unpickler.load() # skip the class metadata
+        return unpickler.load()
+
+    def setGhostState(self, obj, pickle):
+        state = self.getState(pickle)
+        obj.__setstate__(state)
+
+    def getObject(self, pickle):
+        unpickler = self._get_unpickler(pickle)
+        klass, args = unpickler.load()
+        obj = self._new_object(klass, args)
+        state = unpickler.load()
+        obj.__setstate__(state)
+        return obj
+
+class ExternalReference(object):
+    pass
+
+class SimpleObjectReader(BaseObjectReader):
+    """Can be used to inspect a single object pickle.
+
+    It returns an ExternalReference() object for other persistent
+    objects.  It can't instantiate the object.
+    """
+
+    ext_ref = ExternalReference()
+
+    def _persistent_load(self, oid):
+        return self.ext_ref
+
+    def _get_class(self, module, name):
+        return None
+
+class ConnectionObjectReader(BaseObjectReader):
+
+    def __init__(self, conn, cache, factory):
+        self._conn = conn
+        self._cache = cache
+        self._factory = factory
+
+    def _get_class(self, module, name):
+        return self._factory(self._conn, module, name)
+
+    def _persistent_load(self, oid):
+        if isinstance(oid, tuple):
+            # Quick instance reference.  We know all we need to know
+            # to create the instance w/o hitting the db, so go for it!
+            oid, klass_info = oid
+            obj = self._cache.get(oid, None) # XXX it's not a dict
+            if obj is not None:
+                return obj
+
+            klass = self._get_class(*klass_info)
+            # XXX Why doesn't this have args?
+            obj = self._new_object(klass, None)
+            # XXX This doesn't address the last fallback that used to
+            # exist:
+##                    # Eek, we couldn't get the class. Hm.  Maybe there's
+##                    # more current data in the object's actual record!
+##                    return self._conn[oid]
+
+            # XXX should be done by connection
+            obj._p_oid = oid
+            obj._p_jar = self._conn
+            # When an object is created, it is put in the UPTODATE
+            # state.  We must explicitly deactivate it to turn it into
+            # a ghost.
+            obj._p_changed = None
+
+            self._cache[oid] = obj
+            return obj
+
+        obj = self._cache.get(oid)
+        if obj is not None:
+            return obj
+        return self._conn[oid]


=== Zope/lib/python/ZODB/BaseStorage.py 1.36 => 1.37 ===
--- Zope/lib/python/ZODB/BaseStorage.py:1.36	Thu Oct  2 14:17:19 2003
+++ Zope/lib/python/ZODB/BaseStorage.py	Fri Nov 28 11:44:49 2003
@@ -16,8 +16,13 @@
 $Id$
 """
 import cPickle
+import threading
 import time
 
+import UndoLogCompatible
+import POSException
+from persistent.TimeStamp import TimeStamp
+
 import ThreadLock
 import zLOG
 from ZODB import bpthread
@@ -38,12 +43,12 @@
                  "create storage %s" % self.__name__)
 
         # Allocate locks:
-        l=ThreadLock.allocate_lock()
-        self._lock_acquire=l.acquire
-        self._lock_release=l.release
-        l=bpthread.allocate_lock()
-        self._commit_lock_acquire=l.acquire
-        self._commit_lock_release=l.release
+        l = threading.RLock()
+        self._lock_acquire = l.acquire
+        self._lock_release = l.release
+        l = threading.Lock()
+        self._commit_lock_acquire = l.acquire
+        self._commit_lock_release = l.release
 
         t=time.time()
         t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))


=== Zope/lib/python/ZODB/ConflictResolution.py 1.20 => 1.21 ===
--- Zope/lib/python/ZODB/ConflictResolution.py:1.20	Tue Nov 18 08:17:16 2003
+++ Zope/lib/python/ZODB/ConflictResolution.py	Fri Nov 28 11:44:49 2003
@@ -89,14 +89,15 @@
         file = StringIO(newpickle)
         unpickler = Unpickler(file)
         unpickler.persistent_load = prfactory.persistent_load
-        class_tuple = unpickler.load()[0]
+        meta = unpickler.load()
+        class_tuple = meta[0]
         if bad_class(class_tuple):
             return None
         newstate = unpickler.load()
         klass = load_class(class_tuple)
         if klass is None:
             return None
-        inst = klass.__basicnew__()
+        inst = klass.__new__(klass)
 
         try:
             resolve = inst._p_resolveConflict
@@ -112,7 +113,7 @@
         file = StringIO()
         pickler = Pickler(file,1)
         pickler.persistent_id = persistent_id
-        pickler.dump(class_tuple)
+        pickler.dump(meta)
         pickler.dump(resolved)
         return file.getvalue(1)
     except ConflictError:


=== Zope/lib/python/ZODB/Connection.py 1.101 => 1.102 ===
--- Zope/lib/python/ZODB/Connection.py:1.101	Mon Nov  3 13:56:30 2003
+++ Zope/lib/python/ZODB/Connection.py	Fri Nov 28 11:44:49 2003
@@ -15,22 +15,23 @@
 
 $Id$"""
 
-from cPickleCache import PickleCache
-from POSException import ConflictError, ReadConflictError, TransactionError
-from ExtensionClass import Base
-import ExportImport, TmpStore
-from zLOG import LOG, ERROR, BLATHER, WARNING
-from coptimizations import new_persistent_id
-from ConflictResolution import ResolvedSerial
-from Transaction import Transaction, get_transaction
-from ZODB.utils import oid_repr
-
-from cPickle import Unpickler, Pickler
-from cStringIO import StringIO
 import sys
 import threading
 from time import time
-from types import StringType, ClassType
+
+from persistent import PickleCache
+from zLOG import LOG, ERROR, BLATHER, WARNING
+
+from ZODB.ConflictResolution import ResolvedSerial
+from ZODB.coptimizations import new_persistent_id
+from ZODB.ExportImport import ExportImport
+from ZODB.POSException \
+     import ConflictError, ReadConflictError, TransactionError
+from ZODB.TmpStore import TmpStore
+from ZODB.Transaction import Transaction, get_transaction
+from ZODB.utils import oid_repr, z64
+from ZODB.serialize \
+     import ObjectWriter, getClassMetadata, ConnectionObjectReader
 
 global_code_timestamp = 0
 
@@ -43,9 +44,7 @@
     global global_code_timestamp
     global_code_timestamp = time()
 
-ExtensionKlass = Base.__class__
-
-class Connection(ExportImport.ExportImport, object):
+class Connection(ExportImport, object):
     """Object managers for individual object space.
 
     An object space is a version of collection of objects.  In a
@@ -129,87 +128,21 @@
             ver = ''
         return '<Connection at %08x%s>' % (id(self), ver)
 
-    def _breakcr(self):
-        # Persistent objects and the cache don't participate in GC.
-        # Explicitly remove references from the connection to its
-        # cache and to the root object, because they refer back to the
-        # connection.
-        if self._cache is not None:
-            self._cache.clear()
-        self._incrgc = None
-        self.cacheGC = None
-
-    def __getitem__(self, oid, tt=type(())):
+    def __getitem__(self, oid):
         obj = self._cache.get(oid, None)
         if obj is not None:
             return obj
 
-        __traceback_info__ = (oid)
         p, serial = self._storage.load(oid, self._version)
-        __traceback_info__ = (oid, p)
-        file=StringIO(p)
-        unpickler=Unpickler(file)
-        unpickler.persistent_load=self._persistent_load
-
-        object = unpickler.load()
-
-        klass, args = object
-
-        if type(klass) is tt:
-            module, name = klass
-            klass=self._db._classFactory(self, module, name)
-
-        if (args is None or
-            not args and not hasattr(klass,'__getinitargs__')):
-            object=klass.__basicnew__()
-        else:
-            object = klass(*args)
-            if klass is not ExtensionKlass:
-                object.__dict__.clear()
-
-        object._p_oid=oid
-        object._p_jar=self
-        object._p_changed=None
-        object._p_serial=serial
-
-        self._cache[oid] = object
-        return object
-
-    def _persistent_load(self,oid,
-                        tt=type(())):
-
-        __traceback_info__=oid
-
-        if type(oid) is tt:
-            # Quick instance reference.  We know all we need to know
-            # to create the instance wo hitting the db, so go for it!
-            oid, klass = oid
-            obj = self._cache.get(oid, None)
-            if obj is not None:
-                return obj
-
-            if type(klass) is tt:
-                module, name = klass
-                try: klass=self._db._classFactory(self, module, name)
-                except:
-                    # Eek, we couldn't get the class. Hm.
-                    # Maybe their's more current data in the
-                    # object's actual record!
-                    return self[oid]
-
-            object=klass.__basicnew__()
-            object._p_oid=oid
-            object._p_jar=self
-            object._p_changed=None
+        obj = self._reader.getGhost(p)
 
-            self._cache[oid] = object
+        obj._p_oid = oid
+        obj._p_jar = self
+        obj._p_changed = None
+        obj._p_serial = serial
 
-            return object
-
-        obj = self._cache.get(oid, None)
-        if obj is not None:
-            return obj
-        return self[oid]
+        self._cache[oid] = obj
+        return obj
 
     def sortKey(self):
         # XXX will raise an exception if the DB hasn't been set
@@ -224,28 +157,27 @@
 
         Any objects modified since the last transaction are invalidated.
         """
-        self._db=odb
-        self._storage=s=odb._storage
+        self._db = odb
+        self._reader = ConnectionObjectReader(self, self._cache,
+                                              self._db._classFactory)
+        self._storage = odb._storage
         self._sortKey = odb._storage.sortKey
-        self.new_oid=s.new_oid
+        self.new_oid = odb._storage.new_oid
         if self._code_timestamp != global_code_timestamp:
             # New code is in place.  Start a new cache.
             self._resetCache()
         else:
             self._flush_invalidations()
-        self._opened=time()
+        self._opened = time()
 
         return self
 
     def _resetCache(self):
-        '''
-        Creates a new cache, discarding the old.
-        '''
+        """Creates a new cache, discarding the old."""
         self._code_timestamp = global_code_timestamp
         self._invalidated.clear()
-        orig_cache = self._cache
-        orig_cache.clear()
-        self._cache = cache = PickleCache(self, orig_cache.cache_size)
+        cache_size = self._cache.cache_size
+        self._cache = cache = PickleCache(self, cache_size)
         self._incrgc = self.cacheGC = cache.incrgc
 
     def abort(self, object, transaction):
@@ -331,100 +263,31 @@
             # Nothing to do
             return
 
-        stack = [object]
-
-        # Create a special persistent_id that passes T and the subobject
-        # stack along:
-        #
-        # def persistent_id(object,
-        #                   self=self,
-        #                   stackup=stackup, new_oid=self.new_oid):
-        #     if (not hasattr(object, '_p_oid') or
-        #         type(object) is ClassType): return None
-        #
-        #     oid=object._p_oid
-        #
-        #     if oid is None or object._p_jar is not self:
-        #         oid = self.new_oid()
-        #         object._p_jar=self
-        #         object._p_oid=oid
-        #         stackup(object)
-        #
-        #     klass=object.__class__
-        #
-        #     if klass is ExtensionKlass: return oid
-        #
-        #     if hasattr(klass, '__getinitargs__'): return oid
-        #
-        #     module=getattr(klass,'__module__','')
-        #     if module: klass=module, klass.__name__
-        #
-        #     return oid, klass
-
-        file=StringIO()
-        seek=file.seek
-        pickler=Pickler(file,1)
-        pickler.persistent_id=new_persistent_id(self, stack)
-        dbstore=self._storage.store
-        file=file.getvalue
-        cache=self._cache
-        get=cache.get
-        dump=pickler.dump
-        clear_memo=pickler.clear_memo
-
-
-        version=self._version
-
-        while stack:
-            object=stack[-1]
-            del stack[-1]
-            oid=object._p_oid
-            serial=getattr(object, '_p_serial', '\0\0\0\0\0\0\0\0')
-            if serial == '\0\0\0\0\0\0\0\0':
+        w = ObjectWriter(object)
+        for obj in w:
+            oid = obj._p_oid
+            serial = getattr(obj, '_p_serial', z64)
+            if serial == z64:
                 # new object
                 self._creating.append(oid)
             else:
                 #XXX We should never get here
                 if invalid(oid) and not hasattr(object, '_p_resolveConflict'):
-                    raise ConflictError(object=object)
+                    raise ConflictError(object=obj)
                 self._modified.append(oid)
 
-            klass = object.__class__
-
-            if klass is ExtensionKlass:
-                # Yee Ha!
-                dict={}
-                dict.update(object.__dict__)
-                del dict['_p_jar']
-                args=object.__name__, object.__bases__, dict
-                state=None
-            else:
-                if hasattr(klass, '__getinitargs__'):
-                    args = object.__getinitargs__()
-                    len(args) # XXX Assert it's a sequence
-                else:
-                    args = None # New no-constructor protocol!
-
-                module=getattr(klass,'__module__','')
-                if module: klass=module, klass.__name__
-                __traceback_info__=klass, oid, self._version
-                state=object.__getstate__()
-
-            seek(0)
-            clear_memo()
-            dump((klass,args))
-            dump(state)
-            p=file(1)
-            s=dbstore(oid,serial,p,version,transaction)
+            p = w.serialize(obj)
+            s = self._storage.store(oid, serial, p, self._version, transaction)
             self._store_count = self._store_count + 1
             # Put the object in the cache before handling the
             # response, just in case the response contains the
             # serial number for a newly created object
-            try: cache[oid]=object
+            try:
+                self._cache[oid] = obj
             except:
                 # Dang, I bet its wrapped:
-                if hasattr(object, 'aq_base'):
-                    cache[oid]=object.aq_base
+                if hasattr(obj, 'aq_base'):
+                    self._cache[oid] = obj.aq_base
                 else:
                     raise
 
@@ -447,7 +310,6 @@
         load=src.load
         store=tmp.store
         dest=self._version
-        get=self._cache.get
         oids=src._index.keys()
 
         # Copy invalidating and creating info from temporary storage:
@@ -488,11 +350,11 @@
                 del o._p_jar
                 del o._p_oid
 
-    #XXX
-
-    def db(self): return self._db
+    def db(self):
+        return self._db
 
-    def getVersion(self): return self._version
+    def getVersion(self):
+        return self._version
 
     def isReadOnly(self):
         return self._storage.isReadOnly()
@@ -537,7 +399,7 @@
         self.getTransaction().register(object)
 
     def root(self):
-        return self['\0\0\0\0\0\0\0\0']
+        return self[z64]
 
     def setstate(self, obj):
         oid = obj._p_oid
@@ -559,7 +421,7 @@
             p, serial = self._storage.load(oid, self._version)
             self._load_count = self._load_count + 1
             invalid = self._is_invalidated(obj)
-            self._set_ghost_state(obj, p)
+            self._reader.setGhostState(obj, p)
             obj._p_serial = serial
             if invalid:
                 self._handle_independent(obj)
@@ -593,19 +455,6 @@
         finally:
             self._inv_lock.release()
 
-    def _set_ghost_state(self, obj, p):
-        file = StringIO(p)
-        unpickler = Unpickler(file)
-        unpickler.persistent_load = self._persistent_load
-        unpickler.load()
-        state = unpickler.load()
-
-        setstate = getattr(obj, "__setstate__", None)
-        if setstate is None:
-            obj.update(state)
-        else:
-            setstate(state)
-
     def _handle_independent(self, obj):
         # Helper method for setstate() handles possibly independent objects
         # Call _p_independent(), if it returns True, setstate() wins.
@@ -624,42 +473,27 @@
             self.getTransaction().register(obj)
             raise ReadConflictError(object=obj)
 
-    def oldstate(self, object, serial):
-        oid=object._p_oid
-        p = self._storage.loadSerial(oid, serial)
-        file=StringIO(p)
-        unpickler=Unpickler(file)
-        unpickler.persistent_load=self._persistent_load
-        unpickler.load()
-        return  unpickler.load()
-
-    def setklassstate(self, object):
+    def oldstate(self, obj, serial):
+        p = self._storage.loadSerial(obj._p_oid, serial)
+        return self._reader.getState(p)
+
+    def setklassstate(self, obj):
+        # Special case code to handle ZClasses, I think.
+        # Called the cache when an object of type type is invalidated.
         try:
-            oid=object._p_oid
-            __traceback_info__=oid
+            oid = obj._p_oid
             p, serial = self._storage.load(oid, self._version)
-            file=StringIO(p)
-            unpickler=Unpickler(file)
-            unpickler.persistent_load=self._persistent_load
-
-            copy = unpickler.load()
-
-            klass, args = copy
-
-            if klass is not ExtensionKlass:
-                LOG('ZODB',ERROR,
-                    "Unexpected klass when setting class state on %s"
-                    % getattr(object,'__name__','(?)'))
-                return
-
-            copy = klass(*args)
-            object.__dict__.clear()
-            object.__dict__.update(copy.__dict__)
-
-            object._p_oid=oid
-            object._p_jar=self
-            object._p_changed=0
-            object._p_serial=serial
+
+            # We call getGhost(), but we actually get a non-ghost back.
+            # The object is a class, which can't actually be ghosted.
+            copy = self._reader.getGhost(p)
+            obj.__dict__.clear()
+            obj.__dict__.update(copy.__dict__)
+
+            obj._p_oid = oid
+            obj._p_jar = self
+            obj._p_changed = 0
+            obj._p_serial = serial
         except:
             LOG('ZODB',ERROR, 'setklassstate failed', error=sys.exc_info())
             raise
@@ -679,7 +513,7 @@
         if sub:
             # Sub-transaction!
             if self._tmp is None:
-                _tmp = TmpStore.TmpStore(self._version)
+                _tmp = TmpStore(self._version)
                 self._tmp = self._storage
                 self._storage = _tmp
                 _tmp.registerDB(self._db, 0)
@@ -718,7 +552,7 @@
 
         if not store_return:
             return
-        if isinstance(store_return, StringType):
+        if isinstance(store_return, str):
             assert oid is not None
             self._handle_one_serial(oid, store_return, change)
         else:
@@ -726,7 +560,7 @@
                 self._handle_one_serial(oid, serial, change)
 
     def _handle_one_serial(self, oid, serial, change):
-        if not isinstance(serial, StringType):
+        if not isinstance(serial, str):
             raise serial
         obj = self._cache.get(oid, None)
         if obj is None:
@@ -795,8 +629,3 @@
         new._p_changed=1
         self.getTransaction().register(new)
         self._cache[oid]=new
-
-class tConnection(Connection):
-
-    def close(self):
-        self._breakcr()


=== Zope/lib/python/ZODB/DB.py 1.56 => 1.57 ===
--- Zope/lib/python/ZODB/DB.py:1.56	Tue Nov 18 08:17:16 2003
+++ Zope/lib/python/ZODB/DB.py	Fri Nov 28 11:44:49 2003
@@ -84,8 +84,8 @@
             storage.load('\0\0\0\0\0\0\0\0','')
         except KeyError:
             # Create the database's root in the storage if it doesn't exist
-            import PersistentMapping
-            root = PersistentMapping.PersistentMapping()
+            from persistent.mapping import PersistentMapping
+            root = PersistentMapping()
             # Manually create a pickle for the root to put in the storage.
             # The pickle must be in the special ZODB format.
             file = cStringIO.StringIO()
@@ -267,9 +267,6 @@
 
     def close(self):
         self._storage.close()
-        for x, allocated in self._pools[1]:
-            for c in allocated:
-                c._breakcr()
 
     def commitVersion(self, source, destination='', transaction=None):
         if transaction is None:


=== Zope/lib/python/ZODB/DemoStorage.py 1.21 => 1.22 ===
--- Zope/lib/python/ZODB/DemoStorage.py:1.21	Thu Oct  2 14:17:19 2003
+++ Zope/lib/python/ZODB/DemoStorage.py	Fri Nov 28 11:44:49 2003
@@ -83,7 +83,7 @@
 
 import base64, time, string
 from ZODB import POSException, BaseStorage, utils
-from TimeStamp import TimeStamp
+from persistent.TimeStamp import TimeStamp
 from cPickle import loads
 from BTrees import OOBTree
 
@@ -164,12 +164,13 @@
 
         self._lock_acquire()
         try:
-            v=self._vindex.get(src, None)
-            if v is None: return
+            v = self._vindex.get(src)
+            if v is None:
+                return
 
             newserial = self._serial
-            tindex=self._tindex
-            oids=[]
+            tindex = self._tindex
+            oids = []
             for r in v.values():
                 oid, serial, pre, vdata, p = r
                 assert vdata is not None
@@ -180,10 +181,10 @@
                     new_vdata = None
                 tindex.append([oid, newserial, r, new_vdata, p])
 
-
             return oids
 
-        finally: self._lock_release()
+        finally:
+            self._lock_release()
 
     def load(self, oid, version):
         self._lock_acquire()
@@ -249,7 +250,8 @@
                     nv=old
 
                 if serial != oserial:
-                    raise POSException.ConflictError(serials=(oserial, serial))
+                    raise POSException.ConflictError(
+                        oid=oid, serials=(oserial, serial), data=data)
 
             serial=self._serial
             r=[oid, serial, old, version and (version, nv) or None, data]


=== Zope/lib/python/ZODB/ExportImport.py 1.17 => 1.18 ===
--- Zope/lib/python/ZODB/ExportImport.py:1.17	Thu Oct  2 19:58:01 2003
+++ Zope/lib/python/ZODB/ExportImport.py	Fri Nov 28 11:44:49 2003
@@ -60,15 +60,11 @@
     def importFile(self, file, clue='', customImporters=None):
         # This is tricky, because we need to work in a transaction!
 
-        if type(file) is StringType:
-            file_name=file
-            file=open(file,'rb')
-        else:
-            try: file_name=file.name
-            except: file_name='(unknown)'
-        read=file.read
+        if isinstance(file, StringType):
+            file = open(file,'rb')
+        read = file.read
 
-        magic=read(4)
+        magic = read(4)
 
         if magic != 'ZEXP':
             if customImporters and customImporters.has_key(magic):
@@ -77,7 +73,8 @@
             raise POSException.ExportError, 'Invalid export header'
 
         t = self.getTransaction()
-        if clue: t.note(clue)
+        if clue:
+            t.note(clue)
 
         return_oid_list = []
         self.onCommitAction('_importDuringCommit', file, return_oid_list)
@@ -151,7 +148,6 @@
             pickler.dump(unpickler.load())
             pickler.dump(unpickler.load())
             p=newp.getvalue()
-            plen=len(p)
 
             store(oid, None, p, version, transaction)
 


=== Zope/lib/python/ZODB/FileStorage.py 1.139 => 1.140 ===
--- Zope/lib/python/ZODB/FileStorage.py:1.139	Thu Oct  2 18:14:04 2003
+++ Zope/lib/python/ZODB/FileStorage.py	Fri Nov 28 11:44:49 2003
@@ -130,9 +130,9 @@
 # Not all platforms have fsync
 fsync = getattr(os, "fsync", None)
 
+from persistent.TimeStamp import TimeStamp
 from ZODB import BaseStorage, ConflictResolution, POSException
 from ZODB.POSException import UndoError, POSKeyError, MultipleUndoErrors
-from ZODB.TimeStamp import TimeStamp
 from ZODB.lock_file import LockFile
 from ZODB.utils import p64, u64, cp, z64
 from ZODB.fspack import FileStoragePacker
@@ -773,11 +773,13 @@
                     oserial = cached_serial
 
                 if serial != oserial:
-                    data = self.tryToResolveConflict(oid, oserial, serial,
+                    rdata = self.tryToResolveConflict(oid, oserial, serial,
                                                      data)
-                    if data is None:
-                        raise POSException.ConflictError(oid=oid,
-                                                serials=(oserial, serial))
+                    if rdata is None:
+                        raise POSException.ConflictError(
+                            oid=oid, serials=(oserial, serial), data=data)
+                    else:
+                        data = rdata
             else:
                 oserial=serial
 
@@ -2227,7 +2229,6 @@
             self._file.seek(pos)
             h = self._file.read(DATA_HDR_LEN)
             oid, serial, sprev, stloc, vlen, splen = unpack(DATA_HDR, h)
-            prev = u64(sprev)
             tloc = u64(stloc)
             plen = u64(splen)
             dlen = DATA_HDR_LEN + (plen or 8)
@@ -2235,7 +2236,6 @@
             if vlen:
                 dlen += (16 + vlen)
                 tmp = self._file.read(16)
-                pv = u64(tmp[8:16])
                 version = self._file.read(vlen)
             else:
                 version = ''


=== Zope/lib/python/ZODB/MappingStorage.py 1.9 => 1.10 ===
--- Zope/lib/python/ZODB/MappingStorage.py:1.9	Mon Jan 13 19:12:14 2003
+++ Zope/lib/python/ZODB/MappingStorage.py	Fri Nov 28 11:44:49 2003
@@ -26,7 +26,7 @@
 from ZODB import utils
 from ZODB import BaseStorage
 from ZODB import POSException
-from ZODB.TimeStamp import TimeStamp
+from persistent.TimeStamp import TimeStamp
 
 
 class MappingStorage(BaseStorage.BaseStorage):
@@ -71,7 +71,9 @@
                 old = self._index[oid]
                 oserial = old[:8]
                 if serial != oserial:
-                    raise POSException.ConflictError(serials=(oserial, serial))
+                    raise POSException.ConflictError(oid=oid,
+                                                     serials=(oserial, serial),
+                                                     data=data)
 
             serial = self._serial
             self._tindex.append((oid, serial+data))


=== Zope/lib/python/ZODB/Mount.py 1.20 => 1.21 ===
--- Zope/lib/python/ZODB/Mount.py:1.20	Tue Nov 18 08:17:16 2003
+++ Zope/lib/python/ZODB/Mount.py	Fri Nov 28 11:44:49 2003
@@ -16,9 +16,9 @@
 $Id$"""
 __version__='$Revision$'[11:-2]
 
-import thread, Persistence, Acquisition
+import thread, persistent, Acquisition
 from Acquisition import aq_base
-import ExtensionClass, string, time, sys
+import string, time, sys
 from POSException import MountedStorageError
 from zLOG import LOG, ERROR, INFO, WARNING
 
@@ -44,7 +44,7 @@
         return parent_db._classFactory(parent_conn, module, name)
 
 
-class MountPoint(Persistence.Persistent, Acquisition.Implicit):
+class MountPoint(persistent.Persistent, Acquisition.Implicit):
     '''The base class for a Zope object which, when traversed,
     accesses a different database.
     '''


=== Zope/lib/python/ZODB/POSException.py 1.21 => 1.22 ===
--- Zope/lib/python/ZODB/POSException.py:1.21	Thu Oct  2 14:17:19 2003
+++ Zope/lib/python/ZODB/POSException.py	Fri Nov 28 11:44:49 2003
@@ -51,13 +51,17 @@
         related to conflict.  The first is the revision of object that
         is in conflict, the second is the revision of that the current
         transaction read when it started.
+      data : string
+        The database record that failed to commit, used to put the
+        class name in the error message.
 
     The caller should pass either object or oid as a keyword argument,
     but not both of them.  If object is passed, it should be a
     persistent object with an _p_oid attribute.
     """
 
-    def __init__(self, message=None, object=None, oid=None, serials=None):
+    def __init__(self, message=None, object=None, oid=None, serials=None,
+                 data=None):
         if message is None:
             self.message = "database conflict error"
         else:
@@ -75,6 +79,14 @@
             assert self.oid is None
             self.oid = oid
 
+        if data is not None:
+            # avoid circular import chain
+            from ZODB.serialize import SimpleObjectReader
+            self.class_name = SimpleObjectReader().getClassName(data)
+##        else:
+##            if message != "data read conflict error":
+##                raise RuntimeError
+
         self.serials = serials
 
     def __str__(self):
@@ -119,13 +131,66 @@
                                serials=serials)
 
 class BTreesConflictError(ConflictError):
-    """A special subclass for BTrees conflict errors.
+    """A special subclass for BTrees conflict errors."""
 
-    These return an undocumented four-tuple.
-    """
-    def __init__(self, *btree_args):
-        ConflictError.__init__(self, message="BTrees conflict error")
-        self.btree = btree_args
+    msgs = [# 0; i2 or i3 bucket split; positions are all -1
+            'Conflicting bucket split',
+
+            # 1; keys the same, but i2 and i3 values differ, and both values
+            # differ from i1's value
+            'Conflicting changes',
+
+            # 2; i1's value changed in i2, but key+value deleted in i3
+            'Conflicting delete and change',
+
+            # 3; i1's value changed in i3, but key+value deleted in i2
+            'Conflicting delete and change',
+
+            # 4; i1 and i2 both added the same key, or both deleted the
+            # same key
+            'Conflicting inserts or deletes',
+
+            # 5;  i2 and i3 both deleted the same key
+            'Conflicting deletes',
+
+            # 6; i2 and i3 both added the same key
+            'Conflicting inserts',
+
+            # 7; i2 and i3 both deleted the same key, or i2 changed the value
+            # associated with a key and i3 deleted that key
+            'Conflicting deletes, or delete and change',
+
+            # 8; i2 and i3 both deleted the same key, or i3 changed the value
+            # associated with a key and i2 deleted that key
+            'Conflicting deletes, or delete and change',
+
+            # 9; i2 and i3 both deleted the same key
+            'Conflicting deletes',
+
+            # 10; i2 and i3 deleted all the keys, and didn't insert any,
+            # leaving an empty bucket; conflict resolution doesn't have
+            # enough info to unlink an empty bucket from its containing
+            # BTree correctly
+            'Empty bucket from deleting all keys',
+
+            # 11; conflicting changes in an internal BTree node
+            'Conflicting changes in an internal BTree node',
+            ]
+
+    def __init__(self, p1, p2, p3, reason):
+        self.p1 = p1
+        self.p2 = p2
+        self.p3 = p3
+        self.reason = reason
+
+    def __repr__(self):
+        return "BTreesConflictError(%d, %d, %d, %d)" % (self.p1,
+                                                        self.p2,
+                                                        self.p3,
+                                                        self.reason)
+    def __str__(self):
+        return "BTrees conflict error at %d/%d/%d: %s" % (
+            self.p1, self.p2, self.p3, self.msgs[self.reason])
 
 class DanglingReferenceError(TransactionError):
     """An object has a persistent reference to a missing object.


=== Zope/lib/python/ZODB/Transaction.py 1.54 => 1.55 ===
--- Zope/lib/python/ZODB/Transaction.py:1.54	Sun Oct  5 10:25:11 2003
+++ Zope/lib/python/ZODB/Transaction.py	Fri Nov 28 11:44:49 2003
@@ -251,23 +251,27 @@
                 else:
                     self._finish_many(jars)
             except:
-                # Ugh, we got an got an error during commit, so we
-                # have to clean up.  First save the original exception
-                # in case the cleanup process causes another
-                # exception.
-                error = sys.exc_info()
-                try:
-                    self._commit_error(objects, ncommitted, jars, subjars)
-                except:
-                    LOG('ZODB', ERROR,
-                        "A storage error occured during transaction "
-                        "abort.  This shouldn't happen.",
-                        error=sys.exc_info())
-                raise error[0], error[1], error[2]
+                self._cleanup(objects, ncommitted, jars, subjars)
         finally:
             del objects[:] # clear registered
             if not subtransaction and self._id is not None:
                 free_transaction()
+
+    def _cleanup(self, objects, ncommitted, jars, subjars):
+        # Ugh, we got an got an error during commit, so we
+        # have to clean up.  First save the original exception
+        # in case the cleanup process causes another
+        # exception.
+        error = sys.exc_info()
+        try:
+            self._commit_error(objects, ncommitted, jars, subjars)
+        except:
+            LOG("ZODB", ERROR,
+                "A storage error occured during transaction "
+                "abort.  This shouldn't happen.",
+                error=sys.exc_info())
+        raise error[0], error[1], error[2]
+        
 
     def _get_jars(self, objects, subtransaction):
         # Returns a list of jars for this transaction.


=== Zope/lib/python/ZODB/__init__.py 1.25 => 1.26 ===
--- Zope/lib/python/ZODB/__init__.py:1.25	Wed Oct  8 11:02:45 2003
+++ Zope/lib/python/ZODB/__init__.py	Fri Nov 28 11:44:49 2003
@@ -12,36 +12,14 @@
 #
 ##############################################################################
 
-__version__ = '3.3a0'
+__version__ = "3.3a1"
 
 import sys
-import cPersistence, Persistence
-from zLOG import register_subsystem
-
-# This is lame. Don't look. :(
-sys.modules['cPersistence'] = cPersistence
-
-Persistent = cPersistence.Persistent
-
-# Install Persistent and PersistentMapping in Persistence
-if not hasattr(Persistence, 'Persistent'):
-    Persistence.Persistent = Persistent
-    Persistent.__module__ = 'Persistence'
-    Persistence.Overridable = cPersistence.Overridable
-    Persistence.Overridable.__module__ = 'Persistence'
-    if not hasattr(Persistence, 'PersistentMapping'):
-        import PersistentMapping
-        sys.modules['PersistentMapping'] = PersistentMapping
-        sys.modules['BoboPOS'] = sys.modules['ZODB']
-        sys.modules['BoboPOS.PersistentMapping'] = PersistentMapping
-        PersistentMapping = PersistentMapping.PersistentMapping
-        from PersistentMapping import PersistentMapping
-        Persistence.PersistentMapping = PersistentMapping
-        PersistentMapping.__module__ = 'Persistence'
-        del PersistentMapping
-
-del cPersistence
 
+from persistent import TimeStamp
 from DB import DB
-
 import Transaction
+
+# Backward compat for old imports. I don't think TimeStamp should
+# really be in persistent anyway
+sys.modules['ZODB.TimeStamp'] = sys.modules['persistent.TimeStamp']


=== Zope/lib/python/ZODB/conversionhack.py 1.4 => 1.5 ===
--- Zope/lib/python/ZODB/conversionhack.py:1.4	Wed Aug 14 18:07:09 2002
+++ Zope/lib/python/ZODB/conversionhack.py	Fri Nov 28 11:44:49 2003
@@ -12,7 +12,7 @@
 #
 ##############################################################################
 
-import PersistentMapping
+import persistent.mapping
 
 class fixer:
     def __of__(self, parent):
@@ -27,7 +27,7 @@
 hack=hack()
 
 def __basicnew__():
-    r=PersistentMapping.PersistentMapping()
+    r=persistent.mapping.PersistentMapping()
     r.__setstate__=fixer
     return r
 


=== Zope/lib/python/ZODB/coptimizations.c 1.23 => 1.24 ===
--- Zope/lib/python/ZODB/coptimizations.c:1.23	Fri Dec 13 16:56:05 2002
+++ Zope/lib/python/ZODB/coptimizations.c	Fri Nov 28 11:44:49 2003
@@ -16,24 +16,16 @@
 "\n"
 "$Id$\n";
 
-#include "Python.h"
-#define DONT_USE_CPERSISTENCECAPI
 #include "cPersistence.h"
 
-static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
-#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
-#define UNLESS(E) if(!(E))
-#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
-#define OBJECT(O) ((PyObject*)(O))
-
 static PyObject *py__p_oid, *py__p_jar, *py___getinitargs__, *py___module__;
 static PyObject *py_new_oid, *py___class__, *py___name__;
 
 static PyObject *InvalidObjectReference;
 
 typedef struct {
-  PyObject_HEAD
-  PyObject *jar, *stack, *new_oid;
+    PyObject_HEAD
+    PyObject *jar, *stack, *new_oid;
 } persistent_id;
 
 static PyTypeObject persistent_idType;
@@ -74,25 +66,15 @@
 {
     PyObject *class = NULL;
 
-    if (!PyExtensionClass_Check(object)) {
-	if (PyExtensionInstance_Check(object)) {
-	    class = PyObject_GetAttr(object, py___class__);
-	    if (!class) {
-		PyErr_Clear();
-		return 0;
-	    }
-	    /* The __class__ must be an extension class. */
-	    if (!(((PyExtensionClass*)class)->class_flags 
-		  & PERSISTENT_TYPE_FLAG)) {
-		Py_DECREF(class);
-		return 0;
-	    }
-	}
-	else
-	    /* Most objects will exit via this path.  They are neither
-	       extension classes nor instances of them.
-	    */
+    if (!PyType_Check(object)) {
+	if (!PER_TypeCheck(object)) 
+	    return 0;
+
+	class = PyObject_GetAttr(object, py___class__);
+	if (!class) {
+	    PyErr_Clear();
 	    return 0;
+	}
     }
     *out_class = class;
     return 1;
@@ -217,8 +199,7 @@
 	    goto err;
     }
 
-    if (PyExtensionClass_Check(object)
-	|| PyObject_HasAttr(klass, py___getinitargs__))
+    if (PyType_Check(object) || PyObject_HasAttr(klass, py___getinitargs__))
 	goto return_oid;
 
     t2 = get_class_tuple(klass, oid);
@@ -257,29 +238,28 @@
 
 static PyTypeObject persistent_idType = {
     PyObject_HEAD_INIT(NULL)
-    0,				/*ob_size*/
+    0,					/*ob_size*/
     "persistent_id",			/*tp_name*/
     sizeof(persistent_id),		/*tp_basicsize*/
-    0,				/*tp_itemsize*/
-    /* methods */
+    0,					/*tp_itemsize*/
     (destructor)persistent_id_dealloc,	/*tp_dealloc*/
-    (printfunc)0,	/*tp_print*/
-    (getattrfunc)0,		/*obsolete tp_getattr*/
-    (setattrfunc)0,		/*obsolete tp_setattr*/
-    (cmpfunc)0,	/*tp_compare*/
-    (reprfunc)0,		/*tp_repr*/
-    0,		/*tp_as_number*/
-    0,		/*tp_as_sequence*/
-    0,		/*tp_as_mapping*/
-    (hashfunc)0,		/*tp_hash*/
+    0,					/*tp_print*/
+    0,					/*tp_getattr*/
+    0,					/*tp_setattr*/
+    0,					/*tp_compare*/
+    0,					/*tp_repr*/
+    0,					/*tp_as_number*/
+    0,					/*tp_as_sequence*/
+    0,					/*tp_as_mapping*/
+    0,					/*tp_hash*/
     (ternaryfunc)persistent_id_call,	/*tp_call*/
-    (reprfunc)0,		/*tp_str*/
-    (getattrofunc)0,	/*tp_getattro*/
-    (setattrofunc)0,	/*tp_setattro*/
-    
-    /* Space for future expansion */
-    0L,0L,
+    0,					/*tp_str*/
+    0,					/*tp_getattro*/
+    0,					/*tp_setattro*/
+    0,					/* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,			/* tp_flags */
     "C implementation of the persistent_id function defined in Connection.py"
+    					/* tp_doc */
 };
 
 /* End of code for persistent_id objects */
@@ -297,7 +277,7 @@
 void
 initcoptimizations(void)
 {
-    PyObject *m, *d;
+    PyObject *m;
 
 #define make_string(S) if (! (py_ ## S=PyString_FromString(#S))) return
     make_string(_p_oid);
@@ -309,20 +289,23 @@
     make_string(new_oid);
 			
     /* Get InvalidObjectReference error */
-    UNLESS (m=PyString_FromString("ZODB.POSException")) return;
-    ASSIGN(m, PyImport_Import(m));
-    UNLESS (m) return;
-    ASSIGN(m, PyObject_GetAttrString(m, "InvalidObjectReference"));
-    UNLESS (m) return;
-    InvalidObjectReference=m;
+    m = PyImport_ImportModule("ZODB.POSException");
+    if (!m)
+	return;
+    InvalidObjectReference = PyObject_GetAttrString(m, 
+						    "InvalidObjectReference");
+    Py_DECREF(m);
+    if (!InvalidObjectReference)
+	return;
 
-    if (!ExtensionClassImported) 
+    cPersistenceCAPI = PyCObject_Import("persistent.cPersistence", "CAPI");
+    if (!cPersistenceCAPI)
 	return;
 
     m = Py_InitModule3("coptimizations", Module_Level__methods,
 		       coptimizations_doc_string);
-    d = PyModule_GetDict(m);
 
     persistent_idType.ob_type = &PyType_Type;
-    PyDict_SetItemString(d,"persistent_idType", OBJECT(&persistent_idType));
+    Py_INCREF((PyObject *)&persistent_idType);
+    PyModule_AddObject(m, "persistent_idType", (PyObject *)&persistent_idType);
 }


=== Zope/lib/python/ZODB/fsIndex.py 1.4 => 1.5 ===
--- Zope/lib/python/ZODB/fsIndex.py:1.4	Tue Dec  3 13:45:16 2002
+++ Zope/lib/python/ZODB/fsIndex.py	Fri Nov 28 11:44:49 2003
@@ -8,12 +8,11 @@
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE
+# FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Implement an OID to File-position (long integer) mapping
-"""
-#
+"""Implement an OID to File-position (long integer) mapping."""
+
 # To save space, we do two things:
 #
 #     1. We split the keys (OIDS) into 6-byte prefixes and 2-byte suffixes.
@@ -29,29 +28,26 @@
 # suffix to 6-byte data. This should reduce the overall memory usage to
 # 8-16 bytes per OID.
 #
+# Since the mapping from suffix to data contains at most 256 entries,
+# we use a BTree bucket instead of a full BTree to store the results.
+#
 # We use p64 to convert integers to 8-byte strings and lop off the two
 # high-order bytes when saving. On loading data, we add the leading
-# bytes back before using U64 to convert the data back to (long)
+# bytes back before using u64 to convert the data back to (long)
 # integers.
 
-from BTrees._fsBTree import fsBTree as _fsBTree
-
+from __future__ import generators
 import struct
 
-# convert between numbers and six-byte strings
+from BTrees._fsBTree import fsBucket
 
-_t32 = 1L<< 32
+# convert between numbers and six-byte strings
 
 def num2str(n):
-    h, l = divmod(long(n), _t32)
-    return struct.pack(">HI", h, l)
+    return struct.pack(">Q", n)[2:]
 
 def str2num(s):
-    h, l = struct.unpack(">HI", s)
-    if h:
-        return (long(h) << 32) + l
-    else:
-        return l
+    return struct.unpack(">Q", "\000\000" + s)[0]
 
 class fsIndex:
 
@@ -75,7 +71,7 @@
         treekey = key[:6]
         tree = self._data.get(treekey)
         if tree is None:
-            tree = _fsBTree()
+            tree = fsBucket()
             self._data[treekey] = tree
         tree[key[6:]] = value
 
@@ -96,14 +92,19 @@
     def __contains__(self, key):
         tree = self._data.get(key[:6])
         if tree is None:
-            return 0
+            return False
         v = tree.get(key[6:], None)
         if v is None:
-            return 0
-        return 1
+            return False
+        return True
 
     def clear(self):
         self._data.clear()
+
+    def __iter__(self):
+        for prefix, tree in self._data.items():
+            for suffix in tree:
+                yield prefix + suffix
 
     def keys(self):
         r = []


=== Zope/lib/python/ZODB/fsdump.py 1.13 => 1.14 ===
--- Zope/lib/python/ZODB/fsdump.py:1.13	Wed Nov 12 00:00:43 2003
+++ Zope/lib/python/ZODB/fsdump.py	Fri Nov 28 11:44:49 2003
@@ -25,9 +25,9 @@
 from ZODB.FileStorage import TRANS_HDR, TRANS_HDR_LEN
 from ZODB.FileStorage import DATA_HDR, DATA_HDR_LEN, DATA_VERSION_HDR_LEN
 from ZODB.FileStorage import FileIterator
-from ZODB.TimeStamp import TimeStamp
 from ZODB.utils import u64
 from ZODB.tests.StorageTestBase import zodb_unpickle
+from persistent.TimeStamp import TimeStamp
 
 from cPickle import Unpickler
 from cStringIO import StringIO


=== Zope/lib/python/ZODB/fspack.py 1.13 => 1.14 ===
--- Zope/lib/python/ZODB/fspack.py:1.13	Thu Oct  2 20:33:06 2003
+++ Zope/lib/python/ZODB/fspack.py	Fri Nov 28 11:44:49 2003
@@ -177,7 +177,6 @@
                       "txnlen (%d) < headerlen(%d)", th.tlen, th.headerlen())
 
     def checkData(self, th, tpos, dh, pos):
-        tend = tpos + th.tlen
         if dh.tloc != tpos:
             self.fail(pos, "data record does not point to transaction header"
                       ": %d != %d", dh.tloc, tpos)
@@ -345,7 +344,6 @@
         if not prev:
             return None
 
-        pnv = None
         h = self._read_data_header(prev, oid)
         # If the previous record is for a version, it must have
         # a valid pnv.
@@ -712,7 +710,6 @@
         return pos
 
     def copyToPacktime(self):
-        offset = 0L  # the amount of space freed by packing
         pos = self._metadata_size
         new_pos = pos
 
@@ -778,7 +775,6 @@
                 s = th.asString()
                 new_tpos = self._tfile.tell()
                 self._tfile.write(s)
-                new_pos = new_tpos + len(s)
                 copy = 1
 
             if h.plen:
@@ -790,7 +786,6 @@
                 data = self.fetchBackpointer(h.oid, h.back)
 
             self.writePackedDataRecord(h, data, new_tpos)
-            new_pos = self._tfile.tell()
 
         return new_tpos, pos
 


=== Zope/lib/python/ZODB/fsrecover.py 1.13 => 1.14 ===
--- Zope/lib/python/ZODB/fsrecover.py:1.13	Thu Oct  2 14:17:19 2003
+++ Zope/lib/python/ZODB/fsrecover.py	Fri Nov 28 11:44:49 2003
@@ -83,7 +83,7 @@
 import getopt, ZODB.FileStorage, struct, time
 from struct import unpack
 from ZODB.utils import t32, p64, u64
-from ZODB.TimeStamp import TimeStamp
+from persistent.TimeStamp import TimeStamp
 from cPickle import loads
 from ZODB.FileStorage import RecordIterator
 
@@ -323,8 +323,8 @@
                         l = len(r.data)
 
                     print "%7d %s %s" % (u64(r.oid), l, r.version)
-                s = ofs.restore(r.oid, r.serial, r.data, r.version,
-                                r.data_txn, txn)
+                ofs.restore(r.oid, r.serial, r.data, r.version, r.data_txn,
+                            txn)
                 nrec += 1
         except (KeyboardInterrupt, SystemExit):
             raise


=== Zope/lib/python/ZODB/fstools.py 1.1 => 1.2 ===
--- Zope/lib/python/ZODB/fstools.py:1.1	Mon Nov 18 15:45:48 2002
+++ Zope/lib/python/ZODB/fstools.py	Fri Nov 28 11:44:49 2003
@@ -24,7 +24,7 @@
 from ZODB.FileStorage import TRANS_HDR, DATA_HDR, TRANS_HDR_LEN, \
      DATA_HDR_LEN, DATA_VERSION_HDR_LEN
 from ZODB.utils import p64, u64
-from ZODB.TimeStamp import TimeStamp
+from persistent.TimeStamp import TimeStamp
 
 class TxnHeader:
     """Object representing a transaction record header.


=== Zope/lib/python/ZODB/utils.py 1.18 => 1.19 ===
--- Zope/lib/python/ZODB/utils.py:1.18	Thu Oct  2 14:17:19 2003
+++ Zope/lib/python/ZODB/utils.py	Fri Nov 28 11:44:49 2003
@@ -13,7 +13,8 @@
 ##############################################################################
 
 import sys
-import TimeStamp, time
+import time
+from persistent.TimeStamp import TimeStamp
 
 from struct import pack, unpack
 from types import StringType
@@ -80,7 +81,7 @@
 
 
 def newTimeStamp(old=None,
-                 TimeStamp=TimeStamp.TimeStamp,
+                 TimeStamp=TimeStamp,
                  time=time.time, gmtime=time.gmtime):
     t = time()
     ts = TimeStamp(gmtime(t)[:5]+(t%60,))

=== Removed File Zope/lib/python/ZODB/PersistentList.py ===

=== Removed File Zope/lib/python/ZODB/PersistentMapping.py ===

=== Removed File Zope/lib/python/ZODB/cPersistence.c ===

=== Removed File Zope/lib/python/ZODB/cPersistence.h ===




More information about the Zope-Checkins mailing list