[Zope-Checkins] CVS: Zope3/lib/python/Persistence - .cvsignore:1.2 Cache.py:1.2 ICache.py:1.2 IPersistent.py:1.2 IPersistentDataManager.py:1.2 PersistentList.py:1.2 _persistent.py:1.2 _persistentMapping.py:1.2 cPersistence.c:1.2 cPersistence.h:1.2 cPersistenceAPI.h:1.2 setup.py:1.2 __init__.py:1.4

Jim Fulton jim@zope.com
Mon, 10 Jun 2002 19:28:43 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv17445/lib/python/Persistence

Modified Files:
	__init__.py 
Added Files:
	.cvsignore Cache.py ICache.py IPersistent.py 
	IPersistentDataManager.py PersistentList.py _persistent.py 
	_persistentMapping.py cPersistence.c cPersistence.h 
	cPersistenceAPI.h setup.py 
Log Message:
Merged Zope-3x-branch into newly forked Zope3 CVS Tree.


=== Zope3/lib/python/Persistence/.cvsignore 1.1 => 1.2 ===


=== Zope3/lib/python/Persistence/Cache.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from time import time
+from sys import getrefcount
+from weakref import ref
+
+class Cache(object):
+
+    __iter=None
+
+    def __init__(self, size=500, inactive=300):
+        self.__ghosts={}
+        self.__gget=self.__ghosts.get
+        self.__active={}
+        self.__aget=self.__active.get
+        self._size=size
+        self._inactive=300
+
+    def __getitem__(self, oid):
+        o = self.__gget(oid, self)
+        if o is self:
+            o = self.__active[oid]
+        o=o()
+        if o is None:
+            raise KeyError, oid
+        else:
+            return o
+
+    def get(self, oid, default=None):
+        o = self.__gget(oid, self)
+        if o is self:
+            o = self.__active.get(oid, self)
+            if o is self: return default
+        o=o()
+        if o is None:
+            return default
+        else:
+            return o
+
+    def __setitem__(self, oid, object):
+        if object._p_changed is None:
+            # ghost
+            self.__ghosts[oid] = ref(object, _dictdel(oid, self.__ghosts))
+        else:
+            self.__active[oid] = ref(object, _dictdel(oid, self.__active))
+
+    def __delitem__(self, oid):
+        # XXX is there any way to know which dict the key is in?
+        try:
+            del self.__ghosts[oid]
+        except KeyError:
+            pass
+        try:
+            del self.__active[oid]
+        except KeyError:
+            pass
+
+    def __len__(self):
+        return len(self.__ghosts)+len(self.__active)
+
+    def setstate(self, oid, object):
+        try:
+            del self.__ghosts[oid]
+        except KeyError:
+            pass
+        self.__active[oid] = ref(object, _dictdel(oid, self.__active))
+
+    def incrgc(self, multiple=1):
+        na=len(self.__active)
+        if na < 1: return
+
+        # how many objects do we scan?
+        n=min(multiple * max((na-self._size)/10, 3), na)
+        
+        # how long can objects be inactive?
+        inactive = self._inactive * (
+            0.2 + 0.1 * (min(100, 8 * self._size/na))
+            )
+
+        active=self.__active
+        aget=active.get
+        ghosts=self.__ghosts
+        doomed=[]
+
+        now=int(time()%86400)
+
+        i=self.__iter
+        if i is None:
+            i=iter(self.__active)
+
+        while n:
+            n-=1
+            try: oid = i.next()
+            except StopIteration:
+                del self.__iter
+                return
+
+            ob=aget(oid, self)
+            if ob is self: continue
+            ob=ob()
+            state = ob._p_changed
+            
+            if state==0 and abs(ob._p_atime-now) > inactive:
+                doomed.append(oid)
+                continue
+            if state is None:
+                doomed.append(oid)
+
+        for oid in doomed:
+            ob=aget(oid, self)
+            if ob is self: continue
+            ob=ob()
+            ob._p_deactivate()
+            state = ob._p_changed
+            if state is None:
+                del active[oid]
+                ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
+
+    def full_sweep(self):
+        now=int(time()%86400)
+        active=self.__active
+        ghosts=self.__ghosts
+        na=len(active)
+        
+        # how long can objects be inactive?
+        inactive = self._inactive * (
+            0.2 + 0.1 * (min(100, 8 * self._size/na))
+            )
+
+        doomed=[]
+
+        for oid in active:
+            ob=active[oid]
+            ob=ob()
+            state = ob._p_changed
+            if state==0 and abs(ob._p_atime-now) > inactive:
+                doomed.append(oid)
+                continue
+            if state is None:
+                doomed.append(oid)
+
+        for oid in doomed:
+            ob._p_deactivate()
+            state = ob._p_changed
+            if state is None:
+                del active[oid]
+                ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
+
+    def minimize(self):
+        active=self.__active
+        aget=active.get
+        ghosts=self.__ghosts
+
+        # Grump: I cant use an iterator because the size will change
+        # during iteration. :(
+        for oid in active.keys():
+            ob=aget(oid, self)
+            if ob is self: continue
+            ob=ob()
+            ob._p_deactivate()
+            if ob._p_changed is None:
+                del active[oid]
+                ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
+        self.__iter=None
+
+    def invalidate(self, oid):
+        ob = self.__aget(oid, self)
+        if ob is self:
+            return
+        ob = ob()
+        del ob._p_changed
+        del self.__active[oid]
+        ghosts = self.__ghosts
+        ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
+
+    def invalidateMany(self, oids):
+        if oids is None:
+            oids = self.__active.keys()
+        for oid in oids:
+            ob = self.__aget(oid, self)
+            if ob is self:
+                continue
+            ob = ob()
+            del ob._p_changed
+            del self.__active[oid]
+            self.__ghosts[oid] = ref(ob, _dictdel(oid, self.__ghosts))
+
+    def statistics(self):
+        return {
+            'ghosts': len(self.__ghosts),
+            'active': len(self.__active),
+            }
+        
+class _dictdel(object):
+
+    __slots__ = 'oid', 'dict'
+
+    def __init__(self, oid, dict):
+        self.oid, self.dict = oid, dict
+        
+    def __call__(self, *args):
+        del self.dict[self.oid]


=== Zope3/lib/python/Persistence/ICache.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+try:
+    from Interface import Interface
+except ImportError:
+    class Interface: pass
+
+class ICache(Interface):
+    """In-memory object cache
+
+    Cache objects are used by data managers to implement in-memory
+    object caches with automatic object deactivation and removal of
+    unreferenced objects.
+
+    Cache objects depend heavily on the Persistent object C API.
+    """
+
+    def __getitem__(key):
+        """Get a cached object
+        """
+
+    def __setitem__(key, value):
+        """Add an object to the cache
+        """
+
+    def __len__():
+        """Return the number of objects in the cache
+        """
+
+    def get(oid, default=None):
+        """Get a cached object
+        """
+
+    def incrgc(multiple=1):
+        """Perform incremental garbage collection
+
+        An positive integer argument can be provided to speify a
+        number of incremental steps to take.
+        """
+
+    def full_sweep():
+        """Perform a full sweep of the cache
+        """
+
+    def minimize():
+        """Remove as many objects as possible from the cache
+        """
+
+    def invalidate(oids):
+        """Invalidate the object for the given object ids
+        """
+
+    def invalidateMany(oids):
+        """Invalidate the objects for the given colection of object ids
+
+        If oids is None, all of the objets in the cache are
+        invalidated.
+
+        The collection must be iterable as if it was a sequence of oids.
+        """
+
+class ICachePolicy(Interface):
+
+    def maximum_quiet(cache_size):
+        """Return a number of seconds
+
+        Objects that haven't been accessed in the last number seconds
+        should be deactivated.
+        """
+
+    def incremental_check_count(cache_size):
+        """Return the number of objects that should be checked in an
+        incremental garbage collection.
+        """


=== Zope3/lib/python/Persistence/IPersistent.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+try:
+    from Interface import Interface
+    from Interface.Attribute import Attribute
+except ImportError:
+    class Interface: pass
+    def Attribute(*args): return args
+
+class IPersistent(Interface):
+    """Python persistence interface
+
+    Note that there is a separate C API that is not included here.
+    The C API requires a specific data layout and defines an
+    additional state, 'sticky' that is used to pevent object
+    deactivation while in C routines.
+    """
+
+    _p_jar=Attribute(
+        """The data manager for the object
+
+        The data manager implements the IPersistentDataManager interface.
+        If there is no data manager, then this is None.
+        """)
+
+    _p_oid=Attribute(
+        """The object id
+
+        It is up to the data manager to assign this.
+        The special value None is resrved to indicate that an object
+        id has not been assigned.
+        """)
+
+    _p_changed=Attribute(
+        """The persistence state of the object
+
+        This is one of:
+
+        None -- The object is a ghost. It is not active.
+
+        false -- The object is up to date (or has never been saved).
+
+        true -- The object has been modified.
+
+        The object state may be changed by assigning this attribute,
+        however, assigning None is ignored if the object is not in the
+        up-to-date state.
+
+        Note that an object can change to the modified state only if
+        it has a data manager. When such a state change occurs, the
+        'register' method of the data manager is called, passing the
+        persistent object.
+
+        Deleting this attribute forces deactivation independent of
+        existing state.
+
+        Note that an attribute is used for this to allow optimized
+        cache implementations.
+        """)
+
+    _p_serial=Attribute(
+        """The object serial number
+
+        This is an arbitrary object.
+        """)
+
+    _p_atime=Attribute(
+        """The integer object access time, in seconds, modulus one day.
+
+        XXX When does a day start, the current implementation appears
+        to use gmtime, but this hasn't be explicitly specified.
+
+        XXX Why just one day?
+        """)
+
+    def __getstate__():
+        """Get the object state data.
+
+        The state should not include peristent attributes ("_p_name")
+        """
+
+    def __setstate__(state):
+        """Set the object state data
+
+        Note that this does not affect the object's persistence state.
+        """
+    
+    def _p_deactivate():
+        """Deactivate the object
+
+        Change the object to the ghost state is it is in the
+        up-to-date state.
+        """
+
+    def _p_independent():
+        """Hook for subclasses to prevent read conflict errors.
+
+        A specific peristent object type can defined this method and
+        have it return true if the data manager should ignore read
+        conflicts for this object.
+        """


=== Zope3/lib/python/Persistence/IPersistentDataManager.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+try:
+    from Interface import Interface
+except ImportError:
+    class Interface: pass
+
+class IPersistentDataManager(Interface):
+    """Provide services for managing persistent state.
+
+    This interface is provided by ZODB Connections.
+
+    This interface is used by persistent objects.
+    """
+
+    def setstate(object):
+        """Load the state for the given object.
+
+        The object should be in the deactivated (ghost) state.
+        The object's state will be set and the object will end up
+        in the up-to-date state.
+
+        The object must implement the IPersistent interface.
+        """
+
+    def register(object):
+        """Register a IPersistent with the current transaction.
+
+        This method provides some insulation of the persistent object
+        from details of transaction management. For example, it allows
+        the use of per-database-connection rather than per-thread
+        transaction managers.
+        """
+
+    def mtime(object):
+        """Return the modification time of the object.
+
+        The modification time may not be known, in which case None
+        is returned.
+        """


=== Zope3/lib/python/Persistence/PersistentList.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+"""Python implementation of persistent list.
+
+$Id$
+"""
+
+import Persistence
+from UserList import UserList
+
+class PersistentList(UserList, Persistence.Persistent):
+    __super_setitem = UserList.__setitem__
+    __super_delitem = UserList.__delitem__
+    __super_setslice = UserList.__setslice__
+    __super_delslice = UserList.__delslice__
+    __super_iadd = UserList.__iadd__
+    __super_imul = UserList.__imul__
+    __super_append = UserList.append
+    __super_insert = UserList.insert
+    __super_pop = UserList.pop
+    __super_remove = UserList.remove
+    __super_reverse = UserList.reverse
+    __super_sort = UserList.sort
+    __super_extend = UserList.extend
+
+    def __setitem__(self, i, item):
+        self.__super_setitem(i, item)
+        self._p_changed = 1
+
+    def __delitem__(self, i):
+        self.__super_delitem(i)
+        self._p_changed = 1
+
+    def __setslice__(self, i, j, other):
+        self.__super_setslice(i, j, other)
+        self._p_changed = 1
+
+    def __delslice__(self, i, j):
+        self.__super_delslice(i, j)
+        self._p_changed = 1
+    
+    def __iadd__(self, other):
+        self.__super_iadd(other)
+        self._p_changed = 1
+
+    def __imul__(self, n):
+        self.__super_imul(n)
+        self._p_changed = 1
+
+    def append(self, item):
+        self.__super_append(item)
+        self._p_changed = 1
+        
+    def insert(self, i, item):
+        self.__super_insert(i, item)
+        self._p_changed = 1
+
+    def pop(self, i=-1):
+        rtn = self.__super_pop(i)
+        self._p_changed = 1
+        return rtn
+
+    def remove(self, item):
+        self.__super_remove(item)
+        self._p_changed = 1
+        
+    def reverse(self):
+        self.__super_reverse()
+        self._p_changed = 1
+        
+    def sort(self, *args):
+        self.__super_sort(*args)
+        self._p_changed = 1
+
+    def extend(self, other):
+        self.__super_extend(other)
+        self._p_changed = 1
+
+    # This works around a bug in Python 2.1.x (up to 2.1.2 at least) where the
+    # __cmp__ bogusly raises a RuntimeError, and because this is an extension
+    # class, none of the rich comparison stuff works anyway.
+    def __cmp__(self, other):
+        return cmp(self.data, self._UserList__cast(other))


=== Zope3/lib/python/Persistence/_persistent.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from time import time
+from IPersistent import IPersistent
+
+class Persistent(object):
+    """Mix-in class providing IPersistent support
+    """
+
+    __implements__ = IPersistent
+
+    def _p_get_changed(self):
+        return self._p_state
+
+    def _p_set_changed(self, val):
+        if self._p_jar is None or self._p_oid is None:
+            return
+
+        state = self._p_state
+        if state is val: return
+
+        if state:
+            # changed
+            if val==0:
+                self._p_state = 0
+        elif state==0:
+            # unchanged, but not a ghost
+            if val:
+                self._p_state = 1
+                self._p_jar.register(self)
+            elif val is None:
+                self._p_deactivate()
+                self._p_state = None
+        else:
+            # Ghost. Note val can't be None, cuz then val would equal state.
+            self._p_jar.setstate(self)
+            self._p_state = 0
+
+    def _p_del_changed(self):
+        if self._p_jar is None or self._p_oid is None:
+            return
+
+        state = self._p_state
+        if state is not None:
+            self._p_state = 0
+            self._p_deactivate()
+            self._p_state = None
+
+    _p_changed = property(_p_get_changed, _p_set_changed, _p_del_changed,
+                          "set _p_changed to 1 to indicate a change")
+
+    _p_state = 0
+
+    _p_oid = _p_jar = _p_serial = None
+
+    def __getstate__(self):
+        r={}
+        for k, v in self.__dict__.items():
+            if k[:3] not in ('_p_', '_v_'):
+                r[k]=v
+        return r
+
+    def __setstate__(self, state):
+        d=self.__dict__
+        for k, v in d.items():
+            if k[:3] != '_p_':
+                del d[k]
+        d.update(state)
+
+    def _p_deactivate(self):
+        if self._p_state:
+            return
+        if self._p_jar is None or self._p_oid is None:
+            return
+
+        d=self.__dict__
+        for k, v in d.items():
+            if k[:3] != '_p_':
+                del d[k]
+        self._p_state = None
+
+    def __getattribute__(self, name):
+        if name[:3] != '_p_' and name != '__dict__':
+            state=self._p_state
+            if state is None:
+                dm=self._p_jar
+                if dm is not None:
+                    setstate(self, dm, 0)
+
+            object.__setattr__(self, '_p_atime', int(time() % 86400))
+
+        return object.__getattribute__(self, name)
+
+    def __setattr__(self, name, v):
+        if name[:3] not in ('_p_', '_v_') and name != '__dict__':
+            if self._p_state is None:
+                dm=self._p_jar
+                if dm is None or self._p_oid is None:
+                    raise TypeError('Attempt to modify a unreviveable ghost')
+                # revivable ghost
+                setstate(self, dm, 1)
+                dm.register(self)
+            elif not self._p_state:
+                dm=self._p_jar
+                if dm is not None:
+                    self._p_state = 1
+                    dm.register(self)
+
+            self._p_atime = int(time() % 86400)
+
+        return object.__setattr__(self, name, v)
+
+
+def setstate(ob, dm, state):
+    # Put in modified state so we don't mark ourselves as modified
+    # when our state is updated.
+    ob._p_state = 1
+
+    try:
+        # Get out data manager to updates us.
+        dm.setstate(ob)
+
+        # Now set the final state.
+        ob._p_state = state
+
+    except: # We are going to reraise!
+        # Something went wrong. We need to end up in the ghost state:
+        del ob._p_changed
+        raise


=== Zope3/lib/python/Persistence/_persistentMapping.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+"""Python implementation of persistent container type"""
+__version__='$Revision$'[11:-2]
+
+import Persistence
+from UserDict import UserDict
+
+__metaclass__ = type
+
+class PersistentMapping(Persistence.Persistent, UserDict):
+    """A persistent wrapper for mapping objects.
+
+    This class allows wrapping of mapping objects so that object
+    changes are registered.  As a side effect, mapping objects may be
+    subclassed.
+    """
+
+    # UserDict provides all of the mapping behavior.  The
+    # PersistentMapping class is responsible marking the persistent
+    # state as changed when a method actually changes the state.  At
+    # the mapping API evolves, we may need to add more methods here.
+
+    __super_delitem = UserDict.__delitem__
+    __super_setitem = UserDict.__setitem__
+    __super_clear = UserDict.clear
+    __super_update = UserDict.update
+    __super_setdefault = UserDict.setdefault
+    __super_popitem = UserDict.popitem
+
+    __super_p_init = Persistence.Persistent.__init__
+    __super_init = UserDict.__init__
+
+    def __init__(self, dict=None):
+        self.__super_init(dict)
+        self.__super_p_init()
+
+    def __delitem__(self, key):
+        self.__super_delitem(key)
+        self._p_changed = 1
+
+    def __setitem__(self, key, v):
+        self.__super_setitem(key, v)
+        self._p_changed = 1
+
+    def clear(self):
+        self.__super_clear()
+        self._p_changed = 1
+
+    def update(self, b):
+        self.__super_update(b)
+        self._p_changed = 1
+
+    def setdefault(self, key, failobj=None):
+        # We could inline all of UserDict's implementation into the
+        # method here, but I'd rather not depend at all on the
+        # implementation in UserDict (simple as it is).
+        if not self.has_key(key):
+            self._p_changed = 1
+        return self.__super_setdefault(key, failobj)
+
+    def popitem(self):
+        self._p_changed = 1
+        return self.__super_popitem()


=== Zope3/lib/python/Persistence/cPersistence.c 1.1 => 1.2 === (759/859 lines abridged)
+#include "Python.h"
+#include "structmember.h"
+#include "cPersistence.h"
+
+static char PyPersist_doc_string[] =
+"Defines Persistent mixin class for persistent objects.\n"
+"\n"
+"$Id$\n";
+
+/* cPersistence defines two Python types and a C API for them.
+
+   This table summarizes the stuff connected to each type.
+
+   PyTypeObject	   PyPersistBase_Type    	PyPersist_Type
+   supports        persistent objects in C 	persistent objects in Python
+   PyObject *      PyPersistBaseObject *        PyPersistObject *
+   C struct        PyPersist_HEAD		PyPersist_INSTANCE_HEAD
+   C API macro     PyPersist_BASE_TYPE          PyPersist_INSTANCE_TYPE       
+   Python object   Persistence.BasePersistent   Persistence.Persistent
+
+   The two types share many of the same tp_xxx implementations.  The
+   key difference between the base type and the instance type is that
+   the latter has room for an __dict__.  The Persistence.Persistent
+   object is used as a base class for peristent object classes written
+   in Python.
+
+   The functions that can operate on either type take a
+   PyPersistBaseObject * argument.  If the function needs to access
+   the po_dict slot, it must first check obj->ob_type->tp_dictoffset.
+   For a PyPersistBase_Type object, the dictoffset is 0.
+
+   The getstate and setstate implementations only work with
+   PyPersist_Type objects, because they depend on having an __dict__.
+
+   The base type exists to support C types that are also persistent.
+   The only examples of such types are in Persistence.BTrees.
+
+*/
+
+/* A helper for setstate that is more efficient than PyMapping_Keys().
+ */
+
+PyObject *
+_PyPersist_MappingKeys(PyObject *map)
+{
+    static PyObject *s_keys = NULL;
+    PyObject *meth, *args, *keys;
+    if (s_keys == NULL) 
+	s_keys = PyString_InternFromString("keys");
+    meth = PyObject_GetAttr(map, s_keys);

[-=- -=- -=- 759 lines omitted -=- -=- -=-]

+    PyTuple_SET_ITEM(implements, 0, iface);
+    assert(type->tp_dict != NULL);
+    r = PyDict_SetItemString(type->tp_dict, "__implements__", implements);
+ err:
+    Py_XDECREF(mod);
+    Py_XDECREF(iface);
+    Py_XDECREF(implements);
+    return r;
+}
+
+void 
+initcPersistence(void)
+{
+    PyObject *m, *d, *v;
+
+    PyPersistBase_Type.ob_type = &PyType_Type;
+    PyPersist_Type.ob_type = &PyType_Type;
+    if (PyType_Ready(&PyPersistBase_Type) < 0)
+	return;
+    if (PyType_Ready(&PyPersist_Type) < 0)
+	return;
+    if (persist_set_interface(&PyPersistBase_Type) < 0)
+	return;
+    if (persist_set_interface(&PyPersist_Type) < 0)
+	return;
+
+    m = Py_InitModule3("cPersistence", PyPersist_methods, 
+		       PyPersist_doc_string);
+    if (m == NULL)
+	return;
+    d = PyModule_GetDict(m);
+    if (d == NULL)
+	return;
+
+    Py_INCREF(&PyPersist_Type);
+    if (PyDict_SetItemString(d, "Persistent", (PyObject *)&PyPersist_Type) < 0)
+	return;
+
+    Py_INCREF(&PyPersistBase_Type);
+    if (PyDict_SetItemString(d, "BasePersistent", 
+			     (PyObject *)&PyPersistBase_Type) < 0)
+	return;
+
+    v = PyCObject_FromVoidPtr(&c_api, NULL);
+    if (v == NULL)
+	return;
+    if (PyDict_SetItemString(d, "C_API", v) < 0)
+	return;
+    Py_DECREF(v);
+}


=== Zope3/lib/python/Persistence/cPersistence.h 1.1 => 1.2 ===
+
+/* Conceptually an enum is appropriate, but we may want to pack the
+   enum into a very small number of bits -- say 2 or 3.  When we get
+   to this level of optimization, we'll probably need a collection of
+   #define constants.
+*/
+
+enum PyPersist_State { UPTODATE, CHANGED, STICKY, GHOST };
+
+/* The PyPersist_HEAD defines the minimal slots needed by a persistent
+   object.  It exists to support types like BTrees that are defined in
+   C extension modules.
+
+   PyPersistObject is the C extension type used as a mixin for
+   persistent objects defined in Python.  It extends the slots defined
+   by PyPersist_HEAD with a po_dict used to provide __dict__.  The
+   dict is needed for Python instances, but is unnecessary for objects
+   like BTrees.
+*/
+
+#define PyPersist_HEAD \
+    PyObject_HEAD \
+    PyObject *po_dm; \
+    /* XXX oid and serial could be hard-coded as 8-byte strings */ \
+    PyObject *po_oid; \
+    PyObject *po_serial; \
+    int po_atime; \
+    enum PyPersist_State po_state;
+
+#define PyPersist_INSTANCE_HEAD \
+    PyPersist_HEAD \
+    PyObject *po_dict; 
+
+typedef struct {
+    PyPersist_HEAD
+} PyPersistBaseObject;
+
+typedef struct {
+    PyPersist_INSTANCE_HEAD
+} PyPersistObject;
+
+extern PyObject *_PyPersist_Load(PyPersistBaseObject *);
+extern PyObject *_PyPersist_RegisterDataManager(PyPersistBaseObject *);
+extern int _PyPersist_RegisterTransaction(PyPersistBaseObject *);
+extern void _PyPersist_SetATime(PyPersistBaseObject *);
+
+/* A struct to encapsulation the PyPersist C API for use by other
+   dynamically load extensions.
+*/
+
+typedef struct {
+    PyTypeObject *base_type;
+    PyTypeObject *instance_type;
+    PyObject *(*load)(PyPersistBaseObject *);
+    int (*reg_trans)(PyPersistBaseObject *);
+    void (*set_atime)(PyPersistBaseObject *);
+} PyPersist_C_API_struct;
+


=== Zope3/lib/python/Persistence/cPersistenceAPI.h 1.1 => 1.2 ===
+   in the cPersistence extension module to other extension modules.
+   On some (all?) platforms, it isn't possible to have static
+   references to functions and objects defined in other dynamically
+   loaded modules.  The PyPersist_C_API defines a collection of
+   pointers to the shared functions that can be initialized when a
+   module is loaded.
+*/
+
+static PyPersist_C_API_struct *PyPersist_C_API;
+
+#define PyPersist_BASE_TYPE PyPersist_C_API->base_type
+#define PyPersist_INSTANCE_TYPE PyPersist_C_API->instance_type
+
+#define PyPersist_INCREF(O) \
+    if (((O)->po_state == UPTODATE) \
+	|| ((O)->po_state == GHOST \
+	    && PyPersist_C_API->load((PyPersistBaseObject *)(O)))) \
+	(O)->po_state = STICKY;
+
+#define PyPersist_DECREF(O) \
+    { \
+        if ((O)->po_state == STICKY) \
+	    (O)->po_state = UPTODATE; \
+    }
+
+/* XXX need to check *either* sticky or changed for now */
+#define PyPersist_IS_STICKY(O) \
+    ((O)->po_state == STICKY || (O)->po_state == CHANGED)
+
+#define PyPersist_CHANGED(O) \
+    PyPersist_C_API->reg_trans((PyPersistBaseObject *)(O))
+
+#define PyPersist_SetATime(O) \
+    PyPersist_C_API->set_atime((PyPersistBaseObject *)(O))
+
+/* Macros for compatibility with ZODB 3 C extensions. */
+
+#define PER_USE_OR_RETURN(O, R) \
+{ \
+    if (((O)->po_state == GHOST) \
+	&& (PyPersist_C_API->load((PyPersistBaseObject *)(O)) < 0)) { \
+        (O)->po_state = STICKY; \
+	return (R); \
+    } else if ((O)->po_state == UPTODATE) \
+	(O)->po_state = STICKY; \
+}
+
+#define PER_CHANGED(O) PyPersist_C_API->reg_trans((PyPersistBaseObject *)(O))
+
+#define PER_ALLOW_DEACTIVATION(O) \
+{ \
+    if ((O)->po_state == STICKY) \
+	(O)->po_state = UPTODATE; \
+}
+
+#define PER_PREVENT_DEACTIVATION(O) \
+{ \
+    if ((O)->po_state == UPTODATE) \
+	(O)->po_state = STICKY; \
+}
+
+#define PER_USE(O) \
+    ((((PyPersistBaseObject *)(O))->po_state != GHOST) \
+     || (PyPersist_C_API->load((PyPersistBaseObject *)(O)) >= 0) \
+     ? ((((PyPersistBaseObject *)(O))->po_state == UPTODATE) \
+	? (((PyPersistBaseObject *)(O))->po_state = STICKY) : 1) : 0)
+
+#define PER_ACCESSED(O) PyPersist_C_API->set_atime((PyPersistBaseObject *)O)


=== Zope3/lib/python/Persistence/setup.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+# 
+##############################################################################
+from distutils.core import setup, Extension
+
+oob = Extension(name = "BTrees._OOBTree",
+                include_dirs = ["."],
+                sources = ['BTrees/_OOBTree.c'],
+                )
+
+oib = Extension(name = "BTrees._OIBTree",
+                include_dirs = ["."],
+                sources = ['BTrees/_OIBTree.c'],
+                )
+
+iib = Extension(name = "BTrees._IIBTree",
+                include_dirs = ["."],
+                sources = ['BTrees/_IIBTree.c'],
+                define_macros = [('EXCLUDE_INTSET_SUPPORT', None)],
+                )
+
+iob = Extension(name = "BTrees._IOBTree",
+                include_dirs = ["."],
+                sources = ['BTrees/_IOBTree.c'],
+                define_macros = [('EXCLUDE_INTSET_SUPPORT', None)],
+                )
+
+fsb = Extension(name = "BTrees._fsBTree",
+                include_dirs = ["."],
+                sources = ['BTrees/_fsBTree.c'],
+                define_macros = [('EXCLUDE_INTSET_SUPPORT', None)],
+                )
+
+setup(
+    name="Persistence",
+    version="XXX",
+    ext_modules=[Extension("cPersistence", ["cPersistence.c"]),
+                 oob, oib, iib, iob, fsb]
+    )


=== Zope3/lib/python/Persistence/__init__.py 1.3 => 1.4 ===
 # 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.
 # 
 ##############################################################################
-"""Provide access to Persistent and PersistentMapping
+"""Provide access to Persistent and PersistentMapping"""
 
-This avoids dependency on the database package name.
+from cPersistence import Persistent, BasePersistent
 
-While obviously there is nothing in this module, the correct names are
-inserted by the __init__.py in ZODB, jumpstarting the process.
-"""
+# XXX why do we hide the real organization of the package?
+
+from _persistentMapping import PersistentMapping
+PersistentMapping.__module__ = 'Persistence'
+
+from PersistentList import PersistentList
+PersistentList.__module__ = 'Persistence'
+
+__all__ = ["BasePersistent", "Persistent", "PersistentMapping"]