ReusableActions
Make zope.configuration actions re-usable
| Author: | Christian Theune <ct@gocept.com> |
|---|
Motivation
Allow reuse of configuration actions by other configuration mechanisms than ZCML (e.g. grok/martian).
Problem
zope.configuration allows the registration of actions and is able to determine conflicts and defer carrying out the actions after validating the set of actions.
Actions are registered using a discriminator, a callable, the arguments to the callable and an order.
This API is unsuitable to make different configuration mechanisms play nicely together in a reliable way. The action itself can be re-used. However, the discriminator needs to be re-implemented by every configuration mechanism that creates an occurrence of the action.
Proposal
To allow better re-use of actions, I propose to make actions objects that provide the following interface:
class IConfigurationAction(zope.interface.Interface):
discriminator = zope.interface.Attribute(
'A discriminator that identifies this instance of a configuration '
'and allows to detect conflicting actions.')
order = zope.interface.Attribute(
'An indicator on roughly control the order of the execution of '
'actions.')
def __call__(self):
"""Carry out the action."""
Implementations of actions are required to gather additional information on instantiation that can be used from the __call__ method.
In addition, the IConfigurationContext? needs to be extended with a spelling to allow consuming IConfigurationAction? objects. I propose to add use a new method, either record, add or register.
Example
This example considers a rewritten zope.component.zcml.utility.
The current call to context.action() looks like:
>>> _context.action(
... discriminator = ('utility', provides, name),
... callable = handler,
... args = ('registerUtility', component, provides, name),
... )
When using an action class, this could be written as:
>>> configure_utility = UtilityAction(provides, name) >>> _context.add(configure_utility)
The UtilityAction? class could be:
>>> class UtilityAction(object):
...
... zope.interface.implements(IConfigurationAction)
...
... def __init__(self, component, provides, name, order=0):
... self.component = component
... self.provides = provides
... self.name = name
... self.order = order
...
... @property
... def discriminator(self):
... return ('utility', provides, name)
...
... def __call__(self):
... sm = zope.component.getGlobalSiteManager()
... sm.registerUtility(self.component, self.provides, self.name)
(Note that I removed the re-use of getGlobalSiteManager() and the lookup of a method name on it, but this could be easily done using subclassing for sharing implementation between actions.)
