[Zope-dev] A summary of "Interfaces vs ZCA concepts"

Martijn Faassen faassen at startifact.com
Thu Dec 17 10:15:36 EST 2009


Hi there,

Here's a summary of what has been going on in this thread with some 
attempts at conclusions that have support of the consensus so that 
Thomas can proceed

* We want to implement .adapter(), .utility() and .__call__() in 
zope.component as much as possible.

* we want a similar mechanism for each of them to plug in.

* It'd be nice if __call__ came back with a LookupError instead of a 
TypeError, but how to get from A to B without breakage?

* there was some discussion about general plugin points on Interface. 
Those have a complexity cost compared to simply poking the methods into 
the class.

* the methods can be on zope.interface even if zope.component isn't 
installed. They will behave as if the component registry is empty. Their 
behavior should be:

IFoo.adapt(context) raises LookupError, unless the context
provides IFoo, in which case it returns context.

IFoo.adapt(context, default=default) returns default unless context 
provides IFoo, in which case it returns context.

IFoo.utility() raises LookupError.

IFoo.utility(default=default) returns default

What's the behavior of __call__ now if zope.component isn't around?

* Tres brought up that we can come up with a clean plugin interface 
instead, and now I'm tempted to go for that instead of monkey-ing around.

What if there's some global object that zope.component can register into 
that has this API?

class ILookupPlugin(Interface):
    def singleAdapt(interface, context):
        """Look up an adapter that provides interface in the adapter
           registry for context.

        If no adapter can be found, raise LookupError. (
        (or return None or another sentinel? See below for an argument
         in favor of LookupError)
       """

    def multiAdapt(interface, contexts):
       """Look up an adapter that provides interface for contexts.
       """

    def utility(interface):
       """Lookup an utility that provides interface.
       """

And a way to register this into zope.interface (globally):

def registerLookupPlugin(plugin):
     ...

The default plugin provided by zope.interface always raises 
ComponentLookupError (or returns the sentinel). The plugin is not 
responsible for handling defaults, just registry lookups.

Then the code in __call__, adapt and utility can:

* check whether the context (if available) already provides interface, 
return that.

* look up in the plugin

* if the plugin raises (or returns sentinel) and there is a default,
   return default

* if there no default, raise LookupError. I think we can turn the 
message from ComponentLookupError into a LookupError fairly safely, so I 
think having the plugin raise exceptions is better as it retains more 
information. In fact it'd be entirely safe to just reraise 
ComponentLookupError as we only specify LookupError in the API and the 
ComponentLookupError *is* a LookupError

I realize that the proposal for a plugin API gives zope.interface some 
knowledge about adaption and utility lookups, which is what Thomas and 
Wolfgang had trouble with. But after all that's what we're doing anyway 
by putting those methods on the API, one way or another. And we can 
define sensible enough behavior even if zope.component is not installed. 
If someone comes up with *another* implementation besides 
zope.component, say, zope.component2, zope.interface could cleanly deal 
with that too.

[Philosophically from my own perspective I think we'd have much less 
conceptual difficulty with this if we just made __call__ do all lookups, 
as that hides the whole set of concepts of utility versus adapter a bit, 
but we can't get consensus for that unfortunately. But I'm biding my time..]

Regards,

Martijn



More information about the Zope-Dev mailing list