[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