Why is my MultiAdapter failing to register?

53 Views Asked by At

I'm currently experimenting with the ZCA and I've run into a bit of a hitch. The script below defines an IFuncttion interface, which is used to create function objects that can be chained (i.e. monads).

In this example, I'm trying to create a simple writer monad as an adapter, but getMultiAdapter is raising a ComponentLookupError. Why is this?

from zope import component, interface


class IFunction(interface.Interface):
    name = interface.Attribute('Name of the function object')

    def __call__(*args, **kw):
        """Call the function"""


class Function(object):
    name = ''
    interface.implements(IFunction)

    def __call__(self, *args, **kw):
        return self.run(*args, **kw)

    def run(self, *args, **kw):
        raise NotImplementedError


class MWriter(object):
    component.adapts(IFunction, IFunction)
    interface.implements(IFunction)

    def __init__(self, prv, nxt):
        self.prev, self.next = prv, nxt

    def bind(self, x, log=None):
        log = log or []
        result, line = self.prev(x)
        log.append(line)
        return self.next(result, log)

    def __call__(self, *args, **kw):
        return self.bind(*args, **kw)


class AddOne(Function):
    name = 'addone'

    def run(self, x):
        return x + 1


class MulTwo(Function):
    name = 'multwo'

    def run(self, x):
        return x * 2

component.provideAdapter(MWriter)
print component.getMultiAdapter((AddOne(), MulTwo()), MWriter)(11, [])
1

There are 1 best solutions below

0
On BEST ANSWER

You should not pass in the adapter you want to look up to component.getMultiAdapter(). The second argument to that function is the name used for named adapters, but your registration did not use a name.

Simply remove that second argument:

>>> component.getMultiAdapter((AddOne(), MulTwo()))
<__main__.MWriter object at 0x1072516d0>

Unfortunately, calling MWriter() still fails because you expect self.prev() to return a tuple:

result, line = self.prev(x)

but AddOne() returns just the one integer:

class AddOne(Function):
    name = 'addone'

    def run(self, x):
        return x + 1

so you get an exception:

>>> component.getMultiAdapter((AddOne(), MulTwo()))(11)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 37, in __call__
  File "<string>", line 32, in bind
TypeError: 'int' object is not iterable

Perhaps you wanted each Function() to return the name as well as the result:

class Function(object):
    name = ''
    interface.implements(IFunction)

    def __call__(self, *args, **kw):
        return self.run(*args, **kw), self.name

    def run(self, *args, **kw):
        raise NotImplementedError

but then calling self.next() fails because it doesn't accept the extra log argument passed in.