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.)

Deliverables

  1. Create the IConfigurationAction? interface
  2. Change IConfigurationContext? to allow consuming an IConfigurationAction? in addition to the traditional actions.

Todo

  • Decide on spelling of the extended IConfigurationContext? interface



( 97 subscribers )