[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