===================================== Zope 3's Local Component Architecture ===================================== The local component architecture provides several packages that can be used independent of the entire component architecture framework. Thus, I decided to document these frameworks in different files. o Registration Framework (`registration.txt`) Provides an API for creating custom registries. The entries of the registries are managed via registration components. A specific implementation for component-based registrations is also provided. Finally, there are some generic container classes that allow the developer to manage the components and their registrations. o Local Adapter Registry (`adapterregistry.txt`) Provides a persistent adapter registry that uses the registration framework. Local registries can be assembled to a registry tree where nodes further down in the tree overrride registrations of higher-up nodes. o Sites and Local Site Managers (`site.txt`) Provides a local and persistent site manager implementation, so that one can register local utilities and adapters. It uses local adapter registries for its adapter and utility registry. The module also provides some facilities to organize the local software and ensures the correct behavior inside the ZODB. Local Component Architecture API -------------------------------- While the component architecture API provided by `zope.component` is sufficient most of the time, there are a couple of functions that are useful in the context of multiple sites. A very common use case is to get the nearest site manager in a given context. Sometimes, however, one wants the next higher-up site manager. First, let's create a folder tree and create some sites from it: >>> from zope.app.testing import setup >>> root = setup.buildSampleFolderTree() >>> root_sm = setup.createSiteManager(root) >>> folder1_sm = setup.createSiteManager(root['folder1']) If we ask `folder1` for its nearest site manager, we get >>> from zope.app import zapi >>> zapi.getSiteManager(root['folder1']) is folder1_sm True but its next site manager is >>> from zope.app import component >>> component.getNextSiteManager(root['folder1']) is root_sm True The next site manager of the local root is the global site manager: >>> gsm = zapi.getGlobalSiteManager() >>> component.getNextSiteManager(root) is gsm True If a non-location is passed into the function, a component lookup error is raised, since there is no site manager beyond the global site manager: >>> component.getNextSiteManager(object()) Traceback (most recent call last): ... ComponentLookupError: No more site managers have been found. If you use the `queryNextSiteManager()` function, you can specify a `default` return value: >>> component.queryNextSiteManager(object(), 'default') 'default' A related use case to the above is to find the next available utility providing a certain interface and name. This is often used when a utility delegates a query to the next one. Let's start by creating a utility and inserting it in our folder hiearchy: >>> import zope.interface >>> class IMyUtility(zope.interface.Interface): ... pass >>> class MyUtility(object): ... zope.interface.implements(IMyUtility) ... def __init__(self, id): ... self.id = id ... def __repr__(self): ... return "%s('%s')" %(self.__class__.__name__, self.id) >>> gutil = MyUtility('global') >>> gsm.registerUtility(gutil, IMyUtility, 'myutil') >>> util1 = setup.addUtility(folder1_sm, 'myutil', IMyUtility, ... MyUtility('one')) >>> folder1_1_sm = setup.createSiteManager(root['folder1']['folder1_1']) >>> util1_1 = setup.addUtility(folder1_1_sm, 'myutil', IMyUtility, ... MyUtility('one-one')) Now, if we ask `util1_1` for its next available utility and we get >>> component.getNextUtility(util1_1, IMyUtility, 'myutil') MyUtility('one') Next we ask `util1` for its next utility and we should get the global version: >>> component.getNextUtility(util1, IMyUtility, 'myutil') MyUtility('global') However, if we ask the global utility for the next one, an error is raised >>> component.getNextUtility(gutil, IMyUtility, ... 'myutil') #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ComponentLookupError: No more utilities for , 'myutil' have been found. or you can simply use the `queryNextUtility` and specify a default: >>> component.queryNextUtility(gutil, IMyUtility, 'myutil', 'default') 'default' Let's now ensure that the function also works with multiple registries. First we create another base registry: >>> from zope.component import registry >>> myregistry = registry.Components() >>> custom_util = MyUtility('my_custom_util') >>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util') Now we add it as a base to the local site manager: >>> folder1_sm.__bases__ = (myregistry,) + folder1_sm.__bases__ Both, the ``myregistry`` and global utilities should be available: >>> component.queryNextUtility(folder1_sm, IMyUtility, 'my_custom_util') MyUtility('my_custom_util') >>> component.queryNextUtility(folder1_sm, IMyUtility, 'myutil') MyUtility('global')