[Checkins] SVN: zope.interface/trunk/ clean up registry structures to avoid unecessary references to global

Fred Drake fdrake at gmail.com
Tue Jun 30 16:08:57 EDT 2009


Log message for revision 101362:
  clean up registry structures to avoid unecessary references to global
  objects; this makes it easier for those objects to be removed from apps once
  relevant registrations are removed
  

Changed:
  U   zope.interface/trunk/CHANGES.txt
  U   zope.interface/trunk/src/zope/interface/adapter.py
  U   zope.interface/trunk/src/zope/interface/tests/test_adapter.py

-=-
Modified: zope.interface/trunk/CHANGES.txt
===================================================================
--- zope.interface/trunk/CHANGES.txt	2009-06-30 17:58:29 UTC (rev 101361)
+++ zope.interface/trunk/CHANGES.txt	2009-06-30 20:08:57 UTC (rev 101362)
@@ -5,8 +5,12 @@
 3.5.2 (unreleased)
 ==================
 
-- ...
+- BaseAdapterRegistry.unregister, unsubscribe: Remove empty portions of
+  the data structures when something is removed.  This avoids leaving
+  references to global objects (interfaces) that may be slated for
+  removal from the calling application.
 
+
 ==================
 3.5.1 (2009-10-13)
 ==================

Modified: zope.interface/trunk/src/zope/interface/adapter.py
===================================================================
--- zope.interface/trunk/src/zope/interface/adapter.py	2009-06-30 17:58:29 UTC (rev 101361)
+++ zope.interface/trunk/src/zope/interface/adapter.py	2009-06-30 20:08:57 UTC (rev 101362)
@@ -155,10 +155,13 @@
         components = byorder[order]
         key = required + (provided,)
         
+        # Keep track of how we got to `components`:
+        lookups = []
         for k in key:
             d = components.get(k)
             if d is None:
                 return
+            lookups.append((components, k))
             components = d
 
         old = components.get(name)
@@ -168,6 +171,20 @@
             return
 
         del components[name]
+        if not components:
+            # Clean out empty containers, since we don't want our keys
+            # to reference global objects (interfaces) unnecessarily.
+            # This is often a problem when an interface is slated for
+            # removal; a hold-over entry in the registry can make it
+            # difficult to remove such interfaces.
+            for comp, k in reversed(lookups):
+                d = comp[k]
+                if d:
+                    break
+                else:
+                    del comp[k]
+            while byorder and not byorder[-1]:
+                del byorder[-1]
         n = self._provided[provided] - 1
         if n == 0:
             del self._provided[provided]
@@ -177,9 +194,6 @@
 
         self.changed(self)
 
-        return
-
-
     def subscribe(self, required, provided, value):
         required = tuple(map(_convert_None_to_Interface, required))
         name = u''
@@ -216,10 +230,13 @@
         components = byorder[order]
         key = required + (provided,)
         
+        # Keep track of how we got to `components`:
+        lookups = []
         for k in key:
             d = components.get(k)
             if d is None:
                 return
+            lookups.append((components, k))
             components = d
 
         old = components.get(u'')
@@ -233,9 +250,27 @@
 
         if new == old:
             return
-        
-        components[u''] = new
 
+        if new:
+            components[u''] = new
+        else:
+            # Instead of setting components[u''] = new, we clean out
+            # empty containers, since we don't want our keys to
+            # reference global objects (interfaces) unnecessarily.  This
+            # is often a problem when an interface is slated for
+            # removal; a hold-over entry in the registry can make it
+            # difficult to remove such interfaces.
+            if u'' in components:
+                del components[u'']
+            for comp, k in reversed(lookups):
+                d = comp[k]
+                if d:
+                    break
+                else:
+                    del comp[k]
+            while byorder and not byorder[-1]:
+                del byorder[-1]
+
         if provided is not None:
             n = self._provided[provided] + len(new) - len(old)
             if n == 0:

Modified: zope.interface/trunk/src/zope/interface/tests/test_adapter.py
===================================================================
--- zope.interface/trunk/src/zope/interface/tests/test_adapter.py	2009-06-30 17:58:29 UTC (rev 101361)
+++ zope.interface/trunk/src/zope/interface/tests/test_adapter.py	2009-06-30 20:08:57 UTC (rev 101362)
@@ -344,6 +344,63 @@
 
     """
 
+def test_unregister_cleans_up_empties():
+    """
+    >>> class I(zope.interface.Interface):
+    ...     pass
+    >>> class IP(zope.interface.Interface):
+    ...     pass
+    >>> class C(object):
+    ...     pass
+
+    >>> registry = AdapterRegistry()
+
+    >>> registry.register([], IP, '', C)
+    >>> registry.register([I], IP, '', C)
+    >>> registry.register([I], IP, 'name', C)
+    >>> registry.register([I, I], IP, '', C)
+    >>> len(registry._adapters)
+    3
+    >>> map(len, registry._adapters)
+    [1, 1, 1]
+
+    >>> registry.unregister([], IP, '', C)
+    >>> registry.unregister([I], IP, '', C)
+    >>> registry.unregister([I], IP, 'name', C)
+    >>> registry.unregister([I, I], IP, '', C)
+    >>> registry._adapters
+    []
+
+    """
+
+def test_unsubscribe_cleans_up_empties():
+    """
+    >>> class I1(zope.interface.Interface):
+    ...     pass
+    >>> class I2(zope.interface.Interface):
+    ...     pass
+    >>> class IP(zope.interface.Interface):
+    ...     pass
+
+    >>> registry = AdapterRegistry()
+    >>> def handler(event):
+    ...     pass
+
+    >>> registry.subscribe([I1], I1, handler)
+    >>> registry.subscribe([I2], I1, handler)
+    >>> len(registry._subscribers)
+    2
+    >>> map(len, registry._subscribers)
+    [0, 2]
+
+    >>> registry.unsubscribe([I1], I1, handler)
+    >>> registry.unsubscribe([I2], I1, handler)
+    >>> registry._subscribers
+    []
+
+    """
+
+
 def test_suite():
     from zope.testing import doctest, doctestunit
     return unittest.TestSuite((



More information about the Checkins mailing list