zopefoundation / zope.component

Other
32 stars 15 forks source link

not easy to get the superAdapter or next most specific adapter when overriding an adapter. #48

Open djay opened 4 years ago

djay commented 4 years ago

There are times you want to override an adapter but use the fundtionality of the whatever else was registered that you overrode. This code allows you to do that. It would be very handy if this was added as a feature to zope.component, perhaps in a more efficient way?

def superAdapter(specific_interface, adapter, objects, name=u""):
    """ find the next most specific adapter """

    #  We are adjusting view object class to provide IForm rather than IEasyFormForm or IGroup to make
    #  one of the objects less specific. This allows us to find anotehr adapter other than our one. This allows us to
    #  find any custom adapters for any fields that we have overridden
    new_obj = []
    found = False
    for obj in objects:
        interfaces = list(providedBy(obj).interfaces())
        try:
            index = interfaces.index(specific_interface)
            found = True
        except ValueError:
            pass
        else:
            super_inferface = interfaces[index + 1]

            @implementer(super_inferface)
            class Wrapper(object):
                def __init__(self, view):
                    self.__view__ = view

                def __getattr__(self, item):
                    return getattr(self.__view__, item)

            obj = Wrapper(obj)  # Make one class less specific
        new_obj.append(obj)
    if not found:
        return None

    provides = providedBy(adapter).declared[0]

    return queryMultiAdapter(new_obj, provides, name=name)
d-maurer commented 4 years ago

Dylan Jay wrote at 2020-5-21 02:59 -0700:

There are times you want to override an adapter but use the fundtionality of the whatever else was registered that you overrode. This code allows you to do that. It would be very handy if this was added as a feature to zope.component, perhaps in a more efficient way? ...

I had a similar use case in Products.AdvancedQuery. There I need the concept "conditional adapter": an adapter typically good but only if some condition is met. Otherwise, a more general adapter should be used.

I implemented this concept based on "subscription adapter"s. The implementation uses that "subscription adapter"s are delivered in a specific order (when I remember right, most specific first).

jamadden commented 3 years ago

Beginning in zope.interface 5, using super() is now meaningful with adapter lookups. Perhaps that helps with this request?

Make providedBy() and implementedBy() respect super objects. For instance, if class Derived implements IDerived and extends Base which in turn implements IBase, then providedBy(super(Derived, derived)) will return [IBase]. Previously it would have returned [IDerived] (in general, it would previously have returned whatever would have been returned without super).

Along with this change, adapter registries will unpack super objects into their __self___ before passing it to the factory.

Together, this means that component.getAdapter(super(Derived, self), ITarget) is now meaningful.