[Zope-CVS] CVS: Packages/pypes/pypes - extent.py:1.2
Casey Duncan
casey at zope.com
Tue Feb 10 01:26:07 EST 2004
Update of /cvs-repository/Packages/pypes/pypes
In directory cvs.zope.org:/tmp/cvs-serv8526
Modified Files:
extent.py
Log Message:
Begin class extent map implementation
=== Packages/pypes/pypes/extent.py 1.1 => 1.2 ===
--- Packages/pypes/pypes/extent.py:1.1 Mon Feb 9 00:24:51 2004
+++ Packages/pypes/pypes/extent.py Tue Feb 10 01:25:36 2004
@@ -17,18 +17,109 @@
$Id$"""
+from types import ClassType
+from sets import Set
from zope.interface import implements
+from persistent import Persistent
+from BTrees.OOBTree import OOBTree, OOTreeSet
from pypes import services
from pypes.identity import IdRegisteredMessage, IdUnregisteredMessage
-from pypes.interfaces import IExtentService
+from pypes.identity import IdentitySet, listenForIdEvents
+from pypes.interfaces import IExtentService, IExtentMap
from pypes.interfaces import IExtent, ICanonicalExtent, IDerivedExtent
-from persistent import Persistent
+from pypes.exceptions import SetLookupError
-class ClassExtentMap:
+class ClassExtent:
pass
+class ClassExtentMap(Persistent):
+ """Object extents by class"""
+
+ implements(IExtentMap)
+
+ # Extent factory hook for testing/overriding
+ _extent_factory = ClassExtent
+
+ def __init__(self, dbconn):
+ self._subclasses = OOBTree() # Map class key => set of subclass keys
+ self._instances = OOBTree() # Map class key => id set of instances
+ listenForIdEvents(self, dbconn)
+ #self.update(dbconn)
+
+ def __getitem__(self, key):
+ if isinstance(key, type) or isinstance(key, ClassType):
+ try:
+ subclass_keys = self._subclasses[classKey(key)]
+ except KeyError:
+ raise KeyError, key
+ else:
+ return self._extent_factory(key, subclass_keys, self._instances)
+ else:
+ raise TypeError, key
+
+ def __iter__(self):
+ for key in self._instances.keys():
+ # XXX May need to handle mutation
+ yield self[keyClass(key)]
+
+ def update(self):
+ raise NotImplementedError
+
+ ## Non-interface methods ##
+
+ def _notifyIdRegistered(self, message):
+ """Listener for identity registrations"""
+ self._add(message.object)
+
+ def _notifyIdUnregistered(self, message):
+ """Listener for identity removals"""
+ self._remove(message.object)
+
+ def _add(self, obj):
+ """Add obj to the extent map"""
+ cls = getattr(obj, '__class__', type(obj))
+ key = classKey(cls)
+ try:
+ instances = self._instances[key]
+ except KeyError:
+ # Instance of class not yet seen
+ instances = self._instances[key] = IdentitySet()
+ self._addClass(cls)
+ instances.add(obj)
+
+ def _addClass(self, cls):
+ """Update subclass lists for cls"""
+ seen = Set()
+ subclass_key = classKey(cls)
+ for superclass in mro(cls):
+ key = classKey(superclass)
+ try:
+ subclasses = self._subclasses[key]
+ except KeyError:
+ subclasses = self._subclasses[key] = OOTreeSet()
+ subclasses.insert(subclass_key)
+
+ def _remove(self, obj):
+ """Remove obj from the extent map"""
+ key = classKey(obj)
+ try:
+ instances = self._instances[key]
+ instances.remove(obj)
+ except (KeyError, SetLookupError):
+ # No instances of that class known, nothing to do
+ return
+ if not instances:
+ # We need to clean up empty sets to preserve the semantics of
+ # __getitem__, which should not return empty extents (it should
+ # raise KeyError). This makes frequent registration/unregistration
+ # of singletons somewhat more expensive. In return __getitem__ can
+ # be slightly simpler/cheaper which seems worth the trade
+ del self._instances[key]
+ del self._subclasses[key]
+
+
class InterfaceExtentMap:
pass
@@ -49,7 +140,7 @@
"""Create an extent service. By default both class extents and
interface extents are enabled. You may override this by specifying
the appropriate keyword argument. At least one extent type must
- be enabled. dbconn is an open ZODB dbconnection object.
+ be enabled. dbconn is an open ZODB connection object.
"""
assert class_extents or interface_extents, (
'Cannot create service with neither class nor interface '
@@ -82,4 +173,15 @@
yield extent
-
+# XXX These should go to a utils module
+from pypes.event import _mro as mro
+from cPickle import loads
+from cPickle import dumps
+def classKey(obj):
+ if ( isinstance(obj, type) or isinstance(obj, ClassType)):
+ cls = obj
+ else:
+ cls = getattr(obj, '__class__', type(obj))
+ return dumps(cls)
+
+keyClass = loads
More information about the Zope-CVS
mailing list