Python, Zope Component Architecture, Registering an adapter

569 views Asked by At

In a stand-alone python application I use zope.interface, zope.component packages to register and access application's adapters. I thought I could use metaclass concept to register adapters from inside init method of the metaclass. This would "automate" adapter's registration process. Do you see problems with this approach, e.g. using attributes that zope package adds to a class? Thanks in advance for your input.

from zope import component
from zope.interface import Interface, implements


class MetaclassAdapter(type):
    def __init__(cls, clsname, bases, attrs):
        super(MetaclassAdapter, cls).__init__(clsname, bases, attrs)
        component.provideAdapter(cls, cls.__component_adapts__, cls.__implements_advice_data__[0][0])


class IDocument(Interface):
  """Document interface."""

  def title():
    pass

  def author():
    pass

  def content():
    pass

class IPrinter(Interface):
  """Printer interface."""

  def write():
    """Print instance to ..."""


class Printer(object):
  """Adapt instances that provide IDocument interface to IPrinter.
  Print document's attributes to stdout.
  """

  __metaclass__ = MetaclassAdapter
  implements(IPrinter)
  component.adapts(IDocument)

  def __init__(self, context):
    """Store adapted instance that provides IDocument."""
    self.context = context

  def write(self):
    """Serialize document."""
    print 'author: ', self.context.author()
    print 'title: ', self.context.title()
    print 'content: ', self.context.content()



class TextDocument(object):
  implements(IDocument)

  def __init__(self, author, title, content):
    self._author = author
    self._title = title
    self._content = content

  def title(self):
    return self._title

  def author(self):
    return self._author

  def content(self):
    return self._content

# Create instance of TextDocument and store / serialize it to...
IPrinter(TextDocument("Leo T.", "Short Stories", "Once upon a time...")).write()
2

There are 2 answers

1
Tobu On BEST ANSWER

Just because you can, doesn't mean you should.

Registering the adapter is a single line of code outside the class, so I would just do that instead of tucking the behaviour in a metaclass. Explicit is better than implicit.

2
Martijn Pieters On

Edit: go with @Tobu's advice, Don't Do This. My answer below is incorrect but left in place for completeness sake. It is incorrect because the zope.interface.implements metaclass shuffle hasn't processed the interface info as yet.

I think the approach is certainly sane. You do not need to pass in the provided interface or the adapted specs to provideAdapter, the registration method will figure those out as long as there is only one interface implemented:

class MetaclassAdapter(type):
    def __init__(cls, clsname, bases, attrs):
        super(MetaclassAdapter, cls).__init__(clsname, bases, attrs)
        component.provideAdapter(cls)

If you want to support classes that implement more than one interface (by direct declaration or via inheritance) you'll have to come up with semantics to determine what interface gets selected as the adapter target interface.

In that case simply give the selected interface to registerAdapter via the provides= keyword parameter. I would advise you to use the zope.interface introspection APIs (zope.interfaces.implementedBy) to find the provided interfaces rather than directly grabbing them from the internal datastructures on the class.