[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"]