[Checkins] SVN: zope.component/trunk/ Fixed 2 related bugs:
Jim Fulton
jim at zope.com
Tue Jun 2 15:44:19 EDT 2009
Log message for revision 100603:
Fixed 2 related bugs:
When a utility is registered and there was previously a utility
registered for the same interface and name, then the old utility is
unregistered. The 2 bugs related to this:
- There was no ``Unregistered`` for the implicit unregistration. Now
there is.
- The old utility was still held and returned by
getAllUtilitiesRegisteredFor. In other words, it was still
considered registered, eeven though it wasn't. A particularly
negative consequence of this is that the utility is held in memory
or in the database even though it isn't used.
Changed:
U zope.component/trunk/CHANGES.txt
U zope.component/trunk/src/zope/component/registry.py
U zope.component/trunk/src/zope/component/registry.txt
U zope.component/trunk/src/zope/component/tests.py
-=-
Modified: zope.component/trunk/CHANGES.txt
===================================================================
--- zope.component/trunk/CHANGES.txt 2009-06-02 18:44:04 UTC (rev 100602)
+++ zope.component/trunk/CHANGES.txt 2009-06-02 19:44:19 UTC (rev 100603)
@@ -4,9 +4,21 @@
3.7.1 (unreleased)
==================
-- Nothing changed yet.
+- Fixed 2 related bugs:
+ When a utility is registered and there was previously a utility
+ registered for the same interface and name, then the old utility is
+ unregistered. The 2 bugs related to this:
+ - There was no ``Unregistered`` for the implicit unregistration. Now
+ there is.
+
+ - The old utility was still held and returned by
+ getAllUtilitiesRegisteredFor. In other words, it was still
+ considered registered, eeven though it wasn't. A particularly
+ negative consequence of this is that the utility is held in memory
+ or in the database even though it isn't used.
+
3.7.0 (2009-05-21)
==================
@@ -14,7 +26,7 @@
- Add in zope:view and zope:resource implementations into
zope.component.zcml (dependency loaded with zope.component [zcml]).
-
+
3.6.0 (2009-03-12)
==================
@@ -24,7 +36,7 @@
framework were still using those interfaces. They will be adapted
for this change. If you were using some of those interfaces, you
need to adapt your code as well:
-
+
- The IView and IDefaultViewName were moved to zope.publisher.interfaces.
- The IResource was moved to zope.app.publisher.interfaces.
Modified: zope.component/trunk/src/zope/component/registry.py
===================================================================
--- zope.component/trunk/src/zope/component/registry.py 2009-06-02 18:44:04 UTC (rev 100602)
+++ zope.component/trunk/src/zope/component/registry.py 2009-06-02 19:44:19 UTC (rev 100603)
@@ -91,9 +91,11 @@
provided = _getUtilityProvided(component)
reg = self._utility_registrations.get((provided, name))
- if reg is not None and reg[:2] == (component, info):
- # already registered
- return
+ if reg is not None:
+ if reg[:2] == (component, info):
+ # already registered
+ return
+ self.unregisterUtility(reg[0], provided, name)
subscribed = False
for ((p, _), data) in self._utility_registrations.iteritems():
@@ -109,10 +111,12 @@
if event:
notify(Registered(
- UtilityRegistration(self, provided, name, component, info, factory)
+ UtilityRegistration(self, provided, name, component, info,
+ factory)
))
- def unregisterUtility(self, component=None, provided=None, name=u'', factory=None):
+ def unregisterUtility(self, component=None, provided=None, name=u'',
+ factory=None):
if factory:
if component:
raise TypeError("Can't specify factory and component.")
@@ -120,7 +124,8 @@
if provided is None:
if component is None:
- raise TypeError("Must specify one of component, factory and provided")
+ raise TypeError("Must specify one of component, factory and "
+ "provided")
provided = _getUtilityProvided(component)
old = self._utility_registrations.get((provided, name))
@@ -130,6 +135,9 @@
if component is None:
component = old[0]
+
+ # Note that component is now the old thing registered
+
del self._utility_registrations[(provided, name)]
self.utilities.unregister((), provided, name)
@@ -436,7 +444,7 @@
getattr(self.component, '__name__', `self.component`),
self.factory, self.info,
)
-
+
def __cmp__(self, other):
return cmp(self.__repr__(), other.__repr__())
Modified: zope.component/trunk/src/zope/component/registry.txt
===================================================================
--- zope.component/trunk/src/zope/component/registry.txt 2009-06-02 18:44:04 UTC (rev 100602)
+++ zope.component/trunk/src/zope/component/registry.txt 2009-06-02 19:44:19 UTC (rev 100603)
@@ -49,6 +49,8 @@
>>> def factory():
... return tests.U1(1)
>>> components.registerUtility(factory=factory)
+ Unregistered event:
+ UtilityRegistration(<Components comps>, I1, u'', 1, None, u'')
Registered event:
UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at <SOME ADDRESS>>, u'')
@@ -121,6 +123,8 @@
Duplicate registrations replace existing ones:
>>> components.registerUtility(tests.U1(4), info=u'use 4 now')
+ Unregistered event:
+ UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at <SOME ADDRESS>>, u'')
Registered event:
UtilityRegistration(<Components comps>, I1, u'', 4, None, u'use 4 now')
>>> components.getUtility(tests.I1)
@@ -926,6 +930,8 @@
>>> c2.queryUtility(tests.I1)
U1(1)
>>> c1.registerUtility(tests.U1(2))
+ Unregistered event:
+ UtilityRegistration(<Components 1>, I1, u'', 1, None, u'')
Registered event:
UtilityRegistration(<Components 1>, I1, u'', 2, None, u'')
Modified: zope.component/trunk/src/zope/component/tests.py
===================================================================
--- zope.component/trunk/src/zope/component/tests.py 2009-06-02 18:44:04 UTC (rev 100602)
+++ zope.component/trunk/src/zope/component/tests.py 2009-06-02 19:44:19 UTC (rev 100603)
@@ -88,11 +88,11 @@
def __repr__(self):
return "%s%r" % (self.__class__.__name__, self.context)
-
+
class A12_1(A):
component.adapts(I1, I2)
interface.implements(IA1)
-
+
class A12_(A):
component.adapts(I1, I2)
@@ -712,7 +712,7 @@
def test_persistent_component_managers():
"""
-Here, we'll demonstrate that changes work even when data are stored in
+Here, we'll demonstrate that changes work even when data are stored in
a database and when accessed from multiple connections.
Start by setting up a database and creating two transaction
@@ -729,7 +729,7 @@
>>> r2 = c2.root()
Create a set of components registries in the database, alternating
-connections.
+connections.
>>> from zope.component.persistentregistry import PersistentComponents
@@ -761,7 +761,7 @@
>>> r1[2].queryUtility(I1)
U1(1)
>>> t1.commit()
-
+
>>> _ = t2.begin()
>>> r2[1].registerUtility(U1(2))
>>> r2[2].queryUtility(I1)
@@ -771,7 +771,7 @@
U1(2)
>>> t2.commit()
-
+
>>> _ = t1.begin()
>>> r1[1].registerUtility(U12(1), I2)
>>> r1[4].queryUtility(I2)
@@ -789,9 +789,9 @@
>>> r1[1].registerHandler(handle1, info="First handler")
>>> r1[2].registerHandler(handle, required=[U])
-
+
>>> r1[3].registerHandler(handle3)
-
+
>>> r1[4].registerHandler(handle4)
>>> r1[4].handle(U1(1))
@@ -858,7 +858,7 @@
GlobalRegistry.adapters = base
def clear_base():
base.__init__(GlobalRegistry, 'adapters')
-
+
class IFoo(interface.Interface):
pass
class Foo(persistent.Persistent):
@@ -872,12 +872,12 @@
def test_deghostification_of_persistent_adapter_registries():
"""
-
+
We want to make sure that we see updates corrextly.
>>> len(base._v_subregistries)
0
-
+
>>> import ZODB.tests.util
>>> db = ZODB.tests.util.DB()
>>> tm1 = transaction.TransactionManager()
@@ -931,15 +931,17 @@
def test_multi_handler_unregistration():
- """There was a bug where multiple handlers for the same required specification
- would all be removed when one of them was unregistered:
+ """
+ There was a bug where multiple handlers for the same required
+ specification would all be removed when one of them was
+ unregistered:
>>> class I(zope.interface.Interface):
... pass
>>> def factory1(event):
- ... print "| Factory 1 is here"
+ ... print "| Factory 1 is here"
>>> def factory2(event):
- ... print "| Factory 2 is here"
+ ... print "| Factory 2 is here"
>>> class Event(object):
... zope.interface.implements(I)
>>> from zope.component.registry import Components
@@ -960,11 +962,11 @@
It is common for a utility to delegate its answer to a utility
providing the same interface in one of the component registry's
bases. Let's first create a global utility::
-
+
>>> import zope.interface
>>> class IMyUtility(zope.interface.Interface):
... pass
-
+
>>> class MyUtility(ConformsToIComponentLookup):
... zope.interface.implements(IMyUtility)
... def __init__(self, id, sm):
@@ -975,18 +977,18 @@
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
-
+
>>> gutil = MyUtility('global', gsm)
>>> gsm.registerUtility(gutil, IMyUtility, 'myutil')
-
+
Now, let's create two registries and set up the bases hierarchy::
-
+
>>> from zope.component.registry import Components
>>> sm1 = Components('sm1', bases=(gsm, ))
>>> sm1_1 = Components('sm1_1', bases=(sm1, ))
-
+
Now we create two utilities and insert them in our folder hierarchy:
-
+
>>> util1 = MyUtility('one', sm1)
>>> sm1.registerUtility(util1, IMyUtility, 'myutil')
>>> IComponentLookup(util1) is sm1
@@ -996,21 +998,21 @@
>>> sm1_1.registerUtility(util1_1, IMyUtility, 'myutil')
>>> IComponentLookup(util1_1) is sm1_1
True
-
+
Now, if we ask `util1_1` for its next available utility we get the
``one`` utility::
-
+
>>> from zope.component import getNextUtility
>>> getNextUtility(util1_1, IMyUtility, 'myutil')
MyUtility('one')
-
+
Next we ask `util1` for its next utility and we should get the global version:
-
+
>>> getNextUtility(util1, IMyUtility, 'myutil')
MyUtility('global')
-
+
However, if we ask the global utility for the next one, an error is raised
-
+
>>> getNextUtility(gutil, IMyUtility,
... 'myutil') #doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
@@ -1018,35 +1020,68 @@
ComponentLookupError:
No more utilities for <InterfaceClass zope.component.tests.IMyUtility>,
'myutil' have been found.
-
+
You can also use `queryNextUtility` and specify a default:
-
+
>>> from zope.component import queryNextUtility
>>> queryNextUtility(gutil, IMyUtility, 'myutil', 'default')
'default'
-
+
Let's now ensure that the function also works with multiple registries. First
we create another base registry:
-
+
>>> myregistry = Components()
-
+
We now set up another utility into that registry:
-
+
>>> custom_util = MyUtility('my_custom_util', myregistry)
>>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util')
-
+
We add it as a base to the local site manager:
-
+
>>> sm1.__bases__ = (myregistry,) + sm1.__bases__
-
+
Both the ``myregistry`` and global utilities should be available:
-
+
>>> queryNextUtility(sm1, IMyUtility, 'my_custom_util')
MyUtility('my_custom_util')
>>> queryNextUtility(sm1, IMyUtility, 'myutil')
MyUtility('global')
"""
+def dont_leak_utility_registrations_in__subscribers():
+ """
+
+ We've observed utilities getting left in _subscribers when they
+ get unregistered.
+
+ >>> import zope.component.registry
+ >>> reg = zope.component.registry.Components()
+ >>> class C:
+ ... def __init__(self, name):
+ ... self.name = name
+ ... def __repr__(self):
+ ... return "C(%s)" % self.name
+
+ >>> c1 = C(1)
+ >>> reg.registerUtility(c1, I1)
+ >>> reg.registerUtility(c1, I1)
+ >>> list(reg.getAllUtilitiesRegisteredFor(I1))
+ [C(1)]
+
+ >>> reg.unregisterUtility(provided=I1)
+ True
+ >>> list(reg.getAllUtilitiesRegisteredFor(I1))
+ []
+
+ >>> reg.registerUtility(c1, I1)
+ >>> reg.registerUtility(C(2), I1)
+
+ >>> list(reg.getAllUtilitiesRegisteredFor(I1))
+ [C(2)]
+
+ """
+
class StandaloneTests(unittest.TestCase):
def testStandalone(self):
import subprocess
More information about the Checkins
mailing list