summaryrefslogtreecommitdiff
path: root/src/zope
diff options
context:
space:
mode:
authorDan Korostelev <nadako@gmail.com>2009-03-11 23:38:56 +0000
committerDan Korostelev <nadako@gmail.com>2009-03-11 23:38:56 +0000
commit5afc3ad4e288895feb57cbcf42663dcebecd7c80 (patch)
treea92f08925bf9504aa8d73c4c90361ab478f018e4 /src/zope
parent79df7e65fca5515edab2316f89ce1a10f0e3647d (diff)
downloadzope-component-5afc3ad4e288895feb57cbcf42663dcebecd7c80.tar.gz
Add getNextUtility/queryNextUtility functions that used to be in zope.site earlier (and in zope.app.component even more earlier).
Diffstat (limited to 'src/zope')
-rw-r--r--src/zope/component/__init__.py2
-rw-r--r--src/zope/component/_api.py31
-rw-r--r--src/zope/component/interfaces.py14
-rw-r--r--src/zope/component/tests.py92
4 files changed, 139 insertions, 0 deletions
diff --git a/src/zope/component/__init__.py b/src/zope/component/__init__.py
index 3f88ebd..95be229 100644
--- a/src/zope/component/__init__.py
+++ b/src/zope/component/__init__.py
@@ -45,11 +45,13 @@ from zope.component._api import getMultiAdapter
from zope.component._api import getSiteManager
from zope.component._api import getUtilitiesFor
from zope.component._api import getUtility
+from zope.component._api import getNextUtility
from zope.component._api import handle
from zope.component._api import queryAdapter
from zope.component._api import queryAdapterInContext
from zope.component._api import queryMultiAdapter
from zope.component._api import queryUtility
+from zope.component._api import queryNextUtility
from zope.component._api import subscribers
from zope.component._declaration import adaptedBy
diff --git a/src/zope/component/_api.py b/src/zope/component/_api.py
index 4a38a01..1ddc6d8 100644
--- a/src/zope/component/_api.py
+++ b/src/zope/component/_api.py
@@ -181,6 +181,37 @@ def getAllUtilitiesRegisteredFor(interface, context=None):
return getSiteManager(context).getAllUtilitiesRegisteredFor(interface)
+_marker = object()
+
+def queryNextUtility(context, interface, name='', default=None):
+ """Query for the next available utility.
+
+ Find the next available utility providing `interface` and having the
+ specified name. If no utility was found, return the specified `default`
+ value.
+ """
+ sm = getSiteManager(context)
+ bases = sm.__bases__
+ for base in bases:
+ util = base.queryUtility(interface, name, _marker)
+ if util is not _marker:
+ return util
+ return default
+
+
+def getNextUtility(context, interface, name=''):
+ """Get the next available utility.
+
+ If no utility was found, a `ComponentLookupError` is raised.
+ """
+ util = queryNextUtility(context, interface, name, _marker)
+ if util is _marker:
+ raise zope.component.interfaces.ComponentLookupError(
+ "No more utilities for %s, '%s' have been found." % (
+ interface, name))
+ return util
+
+
# Factories
def createObject(__factory_name, *args, **kwargs):
diff --git a/src/zope/component/interfaces.py b/src/zope/component/interfaces.py
index a34b0d6..7823037 100644
--- a/src/zope/component/interfaces.py
+++ b/src/zope/component/interfaces.py
@@ -89,6 +89,20 @@ class IComponentArchitecture(Interface):
the specified interface. If one is not found, returns default.
"""
+ def queryNextUtility(context, interface, name='', default=None):
+ """Query for the next available utility.
+
+ Find the next available utility providing `interface` and having the
+ specified name. If no utility was found, return the specified `default`
+ value.
+ """
+
+ def getNextUtility(context, interface, name=''):
+ """Get the next available utility.
+
+ If no utility was found, a `ComponentLookupError` is raised.
+ """
+
def getUtilitiesFor(interface, context=None):
"""Return the utilities that provide an interface
diff --git a/src/zope/component/tests.py b/src/zope/component/tests.py
index e9adf44..e8627fe 100644
--- a/src/zope/component/tests.py
+++ b/src/zope/component/tests.py
@@ -945,6 +945,98 @@ def test_multi_handler_unregistration():
| Factory 2 is here
"""
+def test_next_utilities():
+ """
+ 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):
+ ... self.id = id
+ ... self.sitemanager = sm
+ ... def __repr__(self):
+ ... return "%s('%s')" % (self.__class__.__name__, self.id)
+
+ >>> 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
+ True
+
+ >>> util1_1 = MyUtility('one-one', sm1_1)
+ >>> 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):
+ ...
+ 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')
+ """
+
class StandaloneTests(unittest.TestCase):
def testStandalone(self):
import subprocess