Author
Status
Problem
Zope 3's component architecture has become simpler over time. We now recognize a small number of core component types:
- Utilities
- Adapters
- Subscribers (and handlers)
For global components, we provide relatively clean APIs for registering components:
provideUtility(component, provides=None, name=u''):
provideAdapter(factory, adapts=None, provides=None, name=''):
provideSubscriptionAdapter(factory, adapts=None, provides=None):
provideHandler(factory, adapts=None):
For local components, the story is substantially more complicated. There are historical reasons for this.
- Originally, we thought that most software would need to be stored
in the ZODB to accommodate software updates in ZEO clusters without
downtime in situations where updates changed data schemas. This
was an overly ambitious goal.
We later realized another strategy for updating ZEO clusters. We can safely update a ZEO cluster in 2 steps:
- Update the cluster with software that uses existing data schemas but that can work with updated schemas.
- Update the cluster again with software that creates data with the new schemas.
- We thought that we needed separate "registration" objects to
manage data beyond basic component registration data, including:
- Registration descriptions
- Component permissions
- Other information such as component factory implementation information.
We now know that:
- Permissions should be handled independently from registration. In most cases, components that are pure software should be public.
- We don't need other information for basic core components.
It is useful to associate a remark with a registration. For example, when registrations are loaded from ZCML, it's helpful to provide (at least optionally) run-time information about where in ZCML the component was registered.
- Originally, we had many more kinds of components than we had now and we needed (or thought we needed) complex registration mechanisms to decide which sort of component an object was.
I think it's time to radically simplify things.
Proposal
I propose to simplify component registration as follows:
- Define a new interface, IComponentRegistry?:
class IComponentRegistry(interface.Interface): """Register components """ def registerUtility(component, provided=None, name=u'', info=u''): """Register a utility component The registered component provided This is the interface provided by the utility. If the component provides a single interface, then this argument is optional and the component-implemented interface will be used. name The utility name. info An object that can be converted to a string to provide information about the registration. """ def unregisterUtility(component=None, provided=None, name=u''): """Unregister a utility A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True. component The registered component The given component can be None, in which case any component registered to provide the given provided interface with the given name is unregistered. provided This is the interface provided by the utility. If the component is not None and provides a single interface, then this argument is optional and the component-implemented interface will be used. name The utility name. """ def registeredUtilities(): """Return an iterable of utility-information objects The information objects will have attributes: provided The provided interface name The name component The registered component info An object that can be converted to a string to provide information about the registration. """ def registerAdapter(factory, required=None, provided=None, name=u'', info=u''): """Register an adapter factory Parameters: factory The object used to compute the adapter required This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute is usually attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory doesn't have a __component_adapts__ adapts attribute, then this argument is required. provided This is the interface provided by the adapter and implemented by the factory. If the factory implements a single interface, then this argument is optional and the factory-implemented interface will be used. name The adapter name. info An object that can be converted to a string to provide information about the registration. """ def unregisterAdapter(factory=None, required=None, provided=None, name=u''): """Register an adapter factory A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True. Parameters: factory This is the object used to compute the adapter. The factory can be None, in which case any factory registered to implement the given provided interface for the given required specifications with the given name is unregistered. required This is a sequence of specifications for objects to be adapted. If the factory is not None and the required arguments is omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory is None or doesn't have a __component_adapts__ adapts attribute, then this argument is required. provided This is the interface provided by the adapter and implemented by the factory. If the factory is not None and implements a single interface, then this argument is optional and the factory-implemented interface will be used. name The adapter name. """ def registeredAdapters(): """Return an iterable of adapter-information objects The adapter information objects will have attributes: required An iterable of required interfaces provided The provided interface name The name component The registered factory info Provide some info about this particular adapter registration. """ def registerSubscriptionAdapter(factory, required=None, provides=None, name=u'', info=''): """Register a subscriber factory Parameters: factory The object used to compute the adapter required This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute is usually attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory doesn't have a __component_adapts__ adapts attribute, then this argument is required. provided This is the interface provided by the adapter and implemented by the factory. If the factory implements a single interface, then this argument is optional and the factory-implemented interface will be used. name The adapter name. Note that this parameter is ignored and is reserved for future use when named subscribers are implemented. info An object that can be converted to a string to provide information about the registration. """ def unregisterSubscriptionAdapter(factory=None, required=None, provides=None, name=u''): """Register a subscriber factory A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True. Parameters: factory This is the object used to compute the adapter. The factory can be None, in which case any factories registered to implement the given provided interface for the given required specifications with the given name are unregistered. required This is a sequence of specifications for objects to be adapted. If the factory is not None and the required arguments is omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory is None or doesn't have a __component_adapts__ adapts attribute, then this argument is required. provided This is the interface provided by the adapter and implemented by the factory. If the factory is not None implements a single interface, then this argument is optional and the factory-implemented interface will be used. name The adapter name. Note that this parameter is ignored and is reserved for future use when named subscribers are implemented. """ def registeredSubscriptionAdapters(): """Return an iterable of subscriber-information objects The subscriber information objects will have attributes: required An iterable of required interfaces provided The provided interface name The name component The registered factory info An object that can be converted to a string to provide information about the registration. """ def registerHandler(handler, adapts=None, name=u'', info=''): """Register a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called. Parameters: handler The object used to handle some event represented by the objects passed to it. required This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute is usually attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory doesn't have a __component_adapts__ adapts attribute, then this argument is required. name The handler name. Note that this parameter is ignored and is reserved for future use when named handlers are implemented. info An object that can be converted to a string to provide information about the registration. """ def unregisterHandler(handler=None, adapts=None, name=u''): """Register a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called. Parameters: handler This is the object used to handle some event represented by the objects passed to it. The handler can be None, in which case any handlers registered for the given required specifications with the given are unregistered. required This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute is usually attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory doesn't have a __component_adapts__ adapts attribute, then this argument is required. name The handler name. Note that this parameter is ignored and is reserved for future use when named handlers are implemented. """ def registeredHandlers(): """Return an iterable of handler-information objects The subscriber information objects will have attributes: required An iterable of required interfaces name The name component The registered handler info An object that can be converted to a string to provide information about the registration. """ - Global and local site managers will provide this interface.
Any object can be registered from anywhere by simply calling one
of the provide methods. It will no longer be necessary to create
separate registration objects. There will no longer be a need for
registration managers. Registries may place restrictions on
objects registered. In particular, local registries stored in the
ZODB will have to be able to store registered objects (or
references to registered objects) in the ZODB.
As far as these APIs are concerned, the location of a registered object won't matter. In fact, a registered object need not be located in a folder at all. Locating components will, however, be encouraged as will registering components only with an immediately including site. I expect that supporting user-interfaces will assume this.
- Local components will not required to provide any special magic interfaces (e.g. ILocalUtility?). Local components will not be required to be annotatable.
- We will still provide site-management folders to allow components to be stored outside of content space if desired.
- We will create new views on all objects available to users with
the zope.ManageSite? permissions. These will allow for
registration of objects as utilities, adapters or subscribers, as
appropriate. It's up to some UI volunteers to work out the
details of this, probably starting with utilities.
Note that there is no need to go through these views to register components. Python code can register components using the above API.
Of course, we will deprecate the old APIs?, which will still work for a time.