[Zope-dev] Interfaces vs ZCA concepts

Tres Seaver tseaver at palladion.com
Wed Dec 16 16:39:13 EST 2009


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Martin Aspeli wrote:
> Tres Seaver wrote:
>> Martin Aspeli wrote: 
>>> 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>)

The "Could not adapt" traceback tells you that it isnt The ZCA doing
that:  it is the C code inside zope.interface.  One might argue that a
better exception would be LookupError:  we haven't "violted the
contract" here.

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

The interface machinery catches whatever the adapter hook raises and
returns a TypeError instead.

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

I can't see anything compelling about making that transform mechanical.
 As noted above, it is already *wrong* to be relying on a specific
exception type here anyway.

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

Why would we bend over backward to keep obviously broken code seeming to
work?

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

Given that the behavior isn't part of any API to date, I'm suggesting
that we *document* the behavior ahead of time (for new code), without
any regard for BBB.  We could document the existing __call__ behavior as
well, if needed.

In either case, I think TypeError (or maybe LookupError) is the *right*
choice:  we don't want to "hide" zope.component's API functions and then
turn around and require folks to import zope.component just to catch its
local exception types.



Tres.
- --
===================================================================
Tres Seaver          +1 540-429-0999          tseaver at palladion.com
Palladion Software   "Excellence by Design"    http://palladion.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkspU4EACgkQ+gerLs4ltQ7koQCgpoHUw8vQZfAQcJec9zWAdKhU
V3kAoIliRpCfujwFSCIs4Gi6z+BIwq0N
=2FUT
-----END PGP SIGNATURE-----



More information about the Zope-Dev mailing list