[Zope-dev] Interfaces vs ZCA concepts

Martin Aspeli optilude+lists at gmail.com
Wed Dec 16 00:00:05 EST 2009


Tres Seaver wrote:

>>> +1 to TypeError:  nobody really cares about the type of the error:  code
>>> that wants to be robust about a failure uses the 'query' methods.  As
>>> long as the message is informative enough (which ComponentLookupError
>>> isn't, really) we should be fine.  If we made CLE derive from TypeError,
>>> we could even still be satisfying the contract.
>> zope.component raises TypeError if you can't adapt. It raises
>> ComponentLookupError it can't find a utility.
>
> Not so:  see $ZSVN/zope.component/trunk/src/zope/component/registry.py:

I'm pretty sure you get a TypeError when Zope can't adapt. I'm not sure 
where it's thrown from, though.

Stupid example:

 >>> from plone.portlets.interfaces import IPortletType
 >>> class X(object): pass
...
 >>> x = ()
 >>> IPortletType(x)
Traceback (most recent call last):
   File "<console>", line 1, in <module>
TypeError: ('Could not adapt', (), <InterfaceClass 
plone.portlets.interfaces.IPortletType>)

>    class Components:
>        ...
>        def getUtility(self, provided, name=u''):
>            utility = self.utilities.lookup((), provided, name)
>            if utility is None:
>                raise ComponentLookupError(provided, name)
>            return utility
>        ...
>        def getAdapter(self, object, interface, name=u''):
>            adapter = self.adapters.queryAdapter(object, interface, name)
>            if adapter is None:
>                raise ComponentLookupError(object, interface, name)
>            return adapter

getAdapter() is different, though:

 >>> from zope.component import getAdapter
 >>> getAdapter(x, IPortletType)
Traceback (most recent call last):
   File "<console>", line 1, in <module>
   File 
"/Users/optilude/.buildout/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py", 
line 98, in getAdapter
     raise ComponentLookupError(object, interface, name)
ComponentLookupError: ((), <InterfaceClass 
plone.portlets.interfaces.IPortletType>, u'')

> which matches the contract spelled out in the docstrings for IComponent.
>   That class raises TypeError only for invalid values passed to the
> various registration functions.

Something else is raising TypeError then. :)

> At any rate, we are talking about errors raised from zope.itnerface
> APIs, which nowhere mention or use CLE::

Ok.

>   $ svn info .  | grep URL
>   URL: svn+ssh://svn.zope.org/repos/main/zope.interface/trunk
>   $ svn up
>   At revision 106615.
>   $ find . -name "*.py" |  xargs grep ComponentLookupError
>   $
>
> Nobody calling an interface today has any *defined* behavior to expect
> in the case of failure (in fact, '__call__' is not even part of IInterface!)
>
>> Let's keep exceptions the same. People do catch specific errors, so
>> those who've done 'except TypeError' now aren't going to be happy if we
>> change that to something else when they try to move to use the "new" API.
>
> Please point to existing code which calls 'IFoo.utility(name="bar")' and
> catches a CLE.  Since this is a new API we are talkign about, there
> can't be any BBB concerns.

True, but we're also going to ask people to change their use of 
getUtility(IFoo) to IFoo.utility and ditto for adaption. If I have 
existing code that looks like this:

try:
     foo = IFoo(bar)
except TypeError:
     pass

I would like to be able to move that "mechanically" to:

try:
    foo = IFoo.adapt(bar)
except TypeError:
    pass

If I have to change the exception type in any of the scenarios (utility 
lookup would be similar) then that would be another change to detect, 
and possibly a harder one to spot in less contrived code.

> Any code today which wants a utility is calling 'getUtilty' (if it
> *knows* the utility must be registered) or 'queryUtility' (if it thinks
> it might not be).  Less facetiously than my first challenge: please
> point to actual code in the wild which looks like::
>
>    try:
>        foo = getUtilty(IFoo, name='bar')
>    except ComponentLookupError:
>        # do something
>
> instead of::
>
>     foo = queryUtility(IFoo, name='bar')
>     if foo is None:
>         # do something
>
> I will argue that any code doing the first, outside of maybe tests of
> the ZCA itself is plain broken.

Perhaps, but I'm pretty sure people have done this. They may also have 
done it in tests.

I'm not religious about it, but I think that if we're only changing the 
API, it'd be preferable not to change the exceptions we throw unless 
semantics also change.

Martin

-- 
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book



More information about the Zope-Dev mailing list