[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)