[Zope3-checkins] CVS: Zope3/src/persistence - __init__.py:1.2 _persistent.py:1.2 cache.py:1.2 dict.py:1.2 interfaces.py:1.2 list.py:1.2 persistence.c:1.2 persistence.h:1.2 persistenceAPI.h:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:13:44 -0500


Update of /cvs-repository/Zope3/src/persistence
In directory cvs.zope.org:/tmp/cvs-serv15352/src/persistence

Added Files:
	__init__.py _persistent.py cache.py dict.py interfaces.py 
	list.py persistence.c persistence.h persistenceAPI.h 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/persistence/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/__init__.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,21 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Provide access to Persistent C extension types."""
+
+from persistence._persistence import Persistent
+from persistence._persistence import PersistentMetaClass
+from persistence._persistence import simple_new
+
+import copy_reg
+copy_reg.constructor(simple_new)


=== Zope3/src/persistence/_persistent.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/_persistent.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,145 @@
+##############################################################################
+#
+# 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 persistence.interfaces import 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
+
+
+
+class Persistent(object):
+    """Mix-in class providing IPersistent support
+    """
+
+    __implements__ = IPersistent
+
+    _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 _p_activate(self):
+        state = self._p_state
+        if state is None:
+            dm = self._p_jar
+            if dm is not None:
+                setstate(self, dm, 0)
+
+    def __getattribute__(self, name):
+        if name[:3] != '_p_' and name != '__dict__':
+            self._p_activate()
+            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/src/persistence/cache.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/cache.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,214 @@
+##############################################################################
+#
+# 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
+
+from persistence.interfaces import ICache
+
+class Cache(object):
+
+    __implements__ = ICache
+
+    __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 = inactive
+
+    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)
+        if ob is None:
+            return
+        ob = ob()
+        del ob._p_changed
+        del self.__active[oid]
+        self.__ghosts[oid] = ref(ob, _dictdel(oid, self.__ghosts))
+
+    def invalidateMany(self, oids):
+        if oids is None:
+            oids = self.__active.keys()
+        for oid in oids:
+            self.invalidate(oid)
+
+    def clear(self):
+        for oid in self.__active.keys():
+            self.invalidate(oid)
+
+    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, ref):
+        del self.dict[self.oid]


=== Zope3/src/persistence/dict.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/dict.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,77 @@
+##############################################################################
+#
+# 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
+
+$Id$
+"""
+
+import persistence
+from UserDict import UserDict
+
+__metaclass__ = type
+
+class PersistentDict(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
+    # PersistentDict 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 = True
+
+    def __setitem__(self, key, v):
+        self.__super_setitem(key, v)
+        self._p_changed = True
+
+    def clear(self):
+        self.__super_clear()
+        self._p_changed = True
+
+    def update(self, b):
+        self.__super_update(b)
+        self._p_changed = True
+
+    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 = True
+        return self.__super_setdefault(key, failobj)
+
+    def popitem(self):
+        self._p_changed = True
+        return self.__super_popitem()


=== Zope3/src/persistence/interfaces.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/interfaces.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,309 @@
+##############################################################################
+#
+# 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 zope.interface import Interface
+from zope.interface import Attribute
+
+class IPersistent(Interface):
+    """Python persistence interface
+
+    A persistent object can eb in one of several states:
+
+    - Unsaved
+
+      The object has been created but not saved in a data manager.
+
+      In this state, the _p_changed attribute is non-None and false
+      and the _p_jar attribute is None.
+
+    - Saved
+
+      The object has been saved and has not been changed since it was saved.
+
+      In this state, the _p_changed attribute is non-None and false
+      and the _p_jar attribute is set to a data manager.
+
+    - Sticky
+
+      This state is identical to the up-to-date state except that the
+      object cannot transition to the ghost state. This is a special
+      state used by C methods of persistent objects to make sure that
+      state is not unloaded in the middle of computation.
+
+      In this state, the _p_changed attribute is non-None and false
+      and the _p_jar attribute is set to a data manager.
+
+      There is, currently, no official way to detect whether an object
+      is in the sticky state.
+
+    - Changed
+
+      The object has been changed.
+
+      In this state, the _p_changed attribute is true
+      and the _p_jar attribute is set to a data manager.
+
+    - Ghost
+
+      the object is in memory but its state has not been loaded from
+      the database (or has been unloaded).  In this state, the object
+      doesn't contain any data.
+
+    The following state transactions are possible:
+
+    - Unsaved -> Saved
+
+      This transition occurs when an object is saved in the
+      database. This usually happens when an unsaved object is added
+      to (e.g. as an attribute ot item of) a saved (or changed) object
+      and the transaction is committed.
+
+    - Saved  -> Changed
+      Sticky -> Changed
+
+      This transition occurs when someone sets an attribute or sets
+      _p_changed to a true value on an up-to-date or sticky
+      object. When the transition occurs, the persistent object is
+      required to call the register method on its data manager,
+      passing itself as the only argument.
+
+    - Saved -> Sticky
+
+      This transition occurs when C code marks the object as sticky to
+      prevent its deactivation and transition to the ghost state.
+
+    - Saved -> Ghost
+
+      This transition occurs when an saved object is deactivated, by:
+      calling _p_deactivate, setting _p_changed to None, or deleting
+      _p_changed.
+
+    - Sticky -> Saved
+
+      This transition occurs when C code unmarks the object as sticky to
+      allow its deactivation and transition to the ghost state.
+
+    - Changed -> Saved
+
+      This transition occurs when a transaction is committed.
+      The data manager affects the transaction by setting _p_changed
+      to a true value.
+
+    - Changed -> Ghost
+
+      This transition occurs when a transaction is aborted.
+      The data manager affects the transaction by deleting _p_changed.
+
+    - Ghost -> Saved
+
+      This transition occurs when an attribute or operation of a ghost
+      is accessed and the object's state is loaded from the database.
+
+    Note that there is a separate C API that is not included here.
+    The C API requires a specific data layout and defines the sticky
+    state 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 saved (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_activate():
+        """Activate the object
+
+        Change the object to the up-to-date state if it is a ghost.
+        """
+
+    def _p_deactivate():
+        """Deactivate the object
+
+        Change the object to the ghost state is it is in the
+        up-to-date state.
+        """
+
+class IPersistentNoReadConflicts(IPersistent):
+    def _p_independent():
+        """Hook for subclasses to prevent read conflict errors
+
+        A specific persistent object type can define this method and
+        have it return true if the data manager should ignore read
+        conflicts for this object.
+        """
+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.
+        """
+
+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/src/persistence/list.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/list.py	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,94 @@
+##############################################################################
+#
+# 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 = True
+
+    def __delitem__(self, i):
+        self.__super_delitem(i)
+        self._p_changed = True
+
+    def __setslice__(self, i, j, other):
+        self.__super_setslice(i, j, other)
+        self._p_changed = True
+
+    def __delslice__(self, i, j):
+        self.__super_delslice(i, j)
+        self._p_changed = True
+
+    def __iadd__(self, other):
+        self.__super_iadd(other)
+        self._p_changed = True
+
+    def __imul__(self, n):
+        self.__super_imul(n)
+        self._p_changed = True
+
+    def append(self, item):
+        self.__super_append(item)
+        self._p_changed = True
+
+    def insert(self, i, item):
+        self.__super_insert(i, item)
+        self._p_changed = True
+
+    def pop(self, i=-1):
+        rtn = self.__super_pop(i)
+        self._p_changed = True
+        return rtn
+
+    def remove(self, item):
+        self.__super_remove(item)
+        self._p_changed = True
+
+    def reverse(self):
+        self.__super_reverse()
+        self._p_changed = True
+
+    def sort(self, *args):
+        self.__super_sort(*args)
+        self._p_changed = True
+
+    def extend(self, other):
+        self.__super_extend(other)
+        self._p_changed = True
+
+    # 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/src/persistence/persistence.c 1.1 => 1.2 === (875/975 lines abridged)
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/persistence.c	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,972 @@
+/* 
+   Copyright (c) 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.
+*/
+ 
+#include "Python.h"
+#include <assert.h>
+#include "structmember.h"
+#include "persistence.h"
+
+static char PyPersist_doc_string[] =
+"Defines Persistent mixin class for persistent objects.\n"
+"\n"
+"$Id$\n";
+
+/* A custom metaclass is only needed to support Python 2.2. */
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 2
+static PyTypeObject PyPersist_MetaType;
+#else
+#define PyPersist_MetaType PyType_Type
+#endif
+
+/* Python version of the simple_new function; */
+static PyObject *py_simple_new = NULL;
+
+/* A helper function that registers a persistent object with its data
+   manager.
+*/
+
+static PyObject *s_register = NULL;
+
+int
+_PyPersist_RegisterDataManager(PyPersistObject *self) 
+{
+    PyObject *meth, *arg, *result;
+
+    if (self->po_dm == NULL)
+	return 0;
+    if (s_register == NULL) 
+	s_register = PyString_InternFromString("register");

[-=- -=- -=- 875 lines omitted -=- -=- -=-]

+    PyPersist_MetaType.tp_new = PyPersist_New;
+    PyPersist_MetaType.tp_base = &PyType_Type;
+    PyPersist_MetaType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+	Py_TPFLAGS_BASETYPE;
+    PyPersist_MetaType.tp_traverse = PyType_Type.tp_traverse;
+    PyPersist_MetaType.tp_clear = PyType_Type.tp_clear;
+    if (PyType_Ready(&PyPersist_MetaType) < 0)
+	return;
+    Py_INCREF(&PyPersist_MetaType);
+    if (PyDict_SetItemString(d, "PersistentMetaClass", 
+			     (PyObject *)&PyPersist_MetaType) < 0)
+	return;
+#else
+    Py_INCREF(&PyType_Type);
+    if (PyDict_SetItemString(d, "PersistentMetaClass", 
+			     (PyObject *)&PyType_Type) < 0)
+	return;
+#endif
+
+    PyPersist_Type.ob_type = &PyPersist_MetaType;
+    PyPersist_Type.tp_new = PyType_GenericNew;
+    if (PyType_Ready(&PyPersist_Type) < 0)
+	return;
+    if (persist_set_interface(&PyPersist_Type) < 0)
+	return;
+
+    Py_INCREF(&PyPersist_Type);
+    if (PyDict_SetItemString(d, "Persistent", (PyObject *)&PyPersist_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);
+
+    if (!insenum(d, "UPTODATE", UPTODATE))
+	return;
+    if (!insenum(d, "CHANGED", CHANGED))
+	return;
+    if (!insenum(d, "STICKY", STICKY))
+	return;
+    if (!insenum(d, "GHOST", GHOST))
+	return;
+
+    py_simple_new = PyMapping_GetItemString(d, "simple_new");
+    if (! py_simple_new)
+        return;
+}


=== Zope3/src/persistence/persistence.h 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/persistence.h	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,61 @@
+/* 
+   Copyright (c) 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.
+*/
+ 
+#include <time.h>
+
+/* 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;
+
+typedef struct {
+    PyPersist_HEAD
+} PyPersistObject;
+
+extern int _PyPersist_Load(PyPersistObject *);
+extern int _PyPersist_RegisterDataManager(PyPersistObject *);
+extern void _PyPersist_SetATime(PyPersistObject *);
+
+/* A struct to encapsulation the PyPersist C API for use by other
+   dynamically load extensions.
+*/
+
+typedef struct {
+    PyTypeObject *base_type;
+    int (*load)(PyPersistObject *);
+    int (*reg_mgr)(PyPersistObject *);
+    void (*set_atime)(PyPersistObject *);
+} PyPersist_C_API_struct;
+


=== Zope3/src/persistence/persistenceAPI.h 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:13:43 2002
+++ Zope3/src/persistence/persistenceAPI.h	Wed Dec 25 09:12:13 2002
@@ -0,0 +1,79 @@
+/* 
+   Copyright (c) 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.
+*/
+ 
+/* The PyPersist_C_API provides access to types and functions defined in
+   the persistence 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_TYPE PyPersist_C_API->base_type
+
+#define PyPersist_INCREF(O) \
+    if (((O)->po_state == UPTODATE) \
+	|| ((O)->po_state == GHOST \
+	    && PyPersist_C_API->load((PyPersistObject *)(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_mgr((PyPersistObject *)(O))
+
+#define PyPersist_SetATime(O) \
+    PyPersist_C_API->set_atime((PyPersistObject *)(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((PyPersistObject *)(O)))) { \
+        (O)->po_state = STICKY; \
+	return (R); \
+    } else if ((O)->po_state == UPTODATE) \
+	(O)->po_state = STICKY; \
+}
+
+#define PER_CHANGED(O) PyPersist_C_API->reg_mgr((PyPersistObject *)(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) \
+    ((((PyPersistObject *)(O))->po_state != GHOST) \
+     || (PyPersist_C_API->load((PyPersistObject *)(O))) \
+     ? ((((PyPersistObject *)(O))->po_state == UPTODATE) \
+	? (((PyPersistObject *)(O))->po_state = STICKY) : 1) : 0)
+
+#define PER_ACCESSED(O) PyPersist_C_API->set_atime((PyPersistObject *)O)