Attention

This document was written for an unsupported version of Plone, Plone 2.1.x, and was last updated 837 days ago.

For more information, see the version support policy.

To learn how to upgrade to the current version of Plone, read the upgrade manual.

Interfaces

by Russ Ferriday last modified Feb 07, 2010 01:17 AM
This section makes explicit the difference between Zope 2 and Zope 3 interfaces, and demonstrates the difference between interfaces implemented in the configure.zcml file and within the class. It's our first look at the ZCML configuration file, so goes into some depth.

Interfaces

Description

This section makes explicit the difference between Zope 2 and Zope 3 interfaces, and demonstrates the difference between interfaces implemented in the configure.zcml file and within the class. It's our first look at the ZCML configuration file, so we go into some depth.

Interfaces are really useful to describe what a class will do. Zope 2 and Zope 3 interfaces both document the "what" without speaking about the "how" but Zope 3 goes further than this and uses the interfaces as components for adapting a class and thus specialize its behaviour.

Zope 2

In the Zope 2 world, interfaces were just meant to document the class and give an overview of the methods and attributes. So you could have a quick look at the different methods of a class and their doc strings without bothering about the implementation.

Zope 2 interfaces inherit from these packages:

    >>> from Interface import Interface
    >>> from Interface import Attribute

So an interface is just a class with methods without code and attributes which inherit from Interface.

By convention, an interface always begin with letter I. Here is a simple interface:

      >>> class ITreeWalker (Interface):
      ...     """
      ...       Walk over a tree
      ...     """
      ...
      ...     my_attribute = Attribute("""A dummy attribute just for example purposes""")
      ...
      ...     def walk(context, path=''):
      ...         """
      ...             walk the whole tree.
      ...         """

Notice that we don't speak about self in the walk method in an interface, as we just describe methods of the class. See also how we define the Attribute in an interface.

We say that a class implements an interface as soon as the class has the same methods and attributes as its interface. A class can have more methods than its interface but not less! There is no problem for a class to implement more than one interface.

So let's create the class which implements the previous interface in the Zope 2 style:

    >>> class SimpleTreeWalker:
    ...     """
    ...       A really simple class which will walk a hierarchy
    ...     """
    ...
    ...     __implements__ = (ITreeWalker)
    ...
    ...     my_attribute = None
    ...
    ...     def walk(self, context, path=''):
    ...         """
    ...             walk the whole tree
    ...         """
    ...         pass

See the __implements__ , that's the Zope 2 way to say that this class implements its interfaces.

Note that Zope/Python won't complain if you didn't correctly implement the interfaces (necessary methods implemented, correct signatures, ...)

So creating a test for this is really useful:

    >>> ITreeWalker.isImplementedByInstancesOf(SimpleTreeWalker)
    1
    >>> from Interface.Verify import verifyClass
    >>> verifyClass(ITreeWalker, SimpleTreeWalker)
    1

We can also test on the instance of the SimpleTreeWalker class:

    >>> simpleTreeWalkerObject = SimpleTreeWalker()
    >>> ITreeWalker.isImplementedBy(simpleTreeWalkerObject)
    1
    >>> from Interface.Verify import verifyObject
    >>> verifyObject(ITreeWalker, simpleTreeWalkerObject)
    1

So we are happy we have an interface and a class which implements the interface correctly.

Zope 3

Creating a Zope 3 interface isn't different from Zope 2, just the inherited classes change.

Zope 3 interfaces inherit from a different package:

    >>> from zope.interface import Interface
    >>> from zope.interface import Attribute

Now bear in mind that all the things coming from zope.XXX are Zope 3 ready.

So we still can use the exact same interface as before:

    >>> class ITreeWalker (Interface):
    ...     """
    ...       Walk over a tree
    ...     """
    ...
    ...     my_attribute = Attribute("""A dummy attribute just for example purposes""")
    ...
    ...     def walk(context, path=''):
    ...         """
    ...             walk the whole tree.
    ...         """

In the Zope 3 style we have two ways to define that a class implements an interface:

  1. The first way can be done from the class itself in the Z2 style
  2. The second way will be very different. We don't do it from the class itself as we did in Z2 but we do it from the ZCML file.

The second one might be a bit more difficult than the first one. Though it's a good way to explain the link between the what and the how outside the how itself.

From the class

sprinting

Here is the simple way to define that a class implements a zope 3 interface:

      >>> from zope.interface import implements
      >>> class SimpleTreeWalker:
      ...     """
      ...       A really simple class which will walk a hierarchy
      ...     """
      ...
      ...     implements(ITreeWalker)
      ...
      ...     my_attribute = None
      ...
      ...     def walk(self, context, path=''):
      ...         """
      ...             walk the whole tree
      ...         """
      ...         pass

And that's all, you don't have to go back to your configure.zcml anymore. Your class implements the Zope 3 interface.

From the ZCML

ZCML digression

We must speak about the Zope 3 ZCML. Zope 3 comes with a new way to configure/wire/glue main Zope 3 components, using XML. This is considered as a good thing for some and as a really painful new thing for others, anyway we are not here to complain, let's just explain.

Here is a simple example zcml configuration file:

        <configure xmlns="http://namespaces.zope.org/zope"
                   xmlns:five="http://namespaces.zope.org/five">
        </configure>

xmlns defines the different namespaces. (W3C definition: "An XML namespace is a collection of names, identified by a URI reference..., which are used in XML documents as element types and attribute names"). We declare here the zope 3 namespace for basic zcml configuration and the five namespace for being able to plug our Zope 2 code with the Zope 3 CA.

This zcml resides in configure.zcml and is parsed upon zope start by Five (in zope 2).

So now that we can play with ZCML let's get back to our interface implementation. Let's create the class which implements the interface:

        >>> class SimpleTreeWalker:
        ...     """
        ...       A really simple class which will walk a hierarchy
        ...     """
        ...
        ...     my_attribute = None
        ...
        ...     def walk(self, context, path=''):
        ...         """
        ...             walk the whole tree
        ...         """
        ...         pass

See that this class is like the Z2 class except that we don't speak about __implements__ anymore! The new way to say that this class implements the ITreeWalker interface is:

      <configure xmlns="http://namespaces.zope.org/zope"
               xmlns:five="http://namespaces.zope.org/five">

         <five:implements
             class="Products.ATContentTypes.adapters.treeWalker.TreeWalker"
             interface="Products.ATContentTypes.interface.treeWalker.ITreeWalker"
             />

      </configure>

Now TreeWalker implements the interface ITreeWalker. Again Zope/Python will not complain if you don't correctly implement your interface so a test is really welcome:

Testing from the SimpleTreeWalker class

  • Zope 2 Interface implementation test was: ITreeWalker.isImplementedByInstanceOf(SimpleTreeWalker)
  • Zope 3 Interface implementation test is: ITreeWalker.implementedBy(SimpleTreeWalker)

Testing from the SimpleTreeWalker object (simpleTreeWalkerObject):

  • Zope 2 Interface implementation test was: ITreeWalker.isImplementedBy()
  • Zope 3 Interface implementation test is: ITreeWalker.providedBy(simpleTreeWalkerObject)

usage:

      >> ITreeWalker.implementedBy(SimpleTreeWalker)
      True
      >> from zope.interface.verify import verifyClass
      >> verifyClass(ITreeWalker, SimpleTreeWalker)
      True

Let's do the test from an instance of the SimpleTreeWalker class:

      >> simpleTreeWalkerObject = SimpleTreeWalker()
      >> ITreeWalker.providedBy(simpleTreeWalkerObject)
      True
      >> from zope.interface.verify import verifyObject
      >> verifyObject(ITreeWalker, simpleTreeWalkerObject)
      True

Again we are so happy, our class implements a Zope 3 interface!

ZCML include directive

sprinting

If you need to define many zcml directives inside your configure.zcml, a good practice is to split up the file using the <include file="myfile.zcml"/> directive.

Example:

         <configure xmlns="http://namespaces.zope.org/zope"
                   xmlns:five="http://namespaces.zope.org/five">

             <include file="implements.zcml"/>

         </configure>

Zope 2 and Zope 3 Interfaces file location

In ATCT, Zope 2 interfaces are defined in the interfaces.py file. All Zope 3 interfaces are in the folder interface. There isn't yet a definitive common practice convention on where to put the interfaces (even in Zope 3).

Zope 3 will force you to write interfaces (which is a good practice) as interfaces won't be just for documentation purposes, they are also the starting point for getting the adapter. Let's see that closer now.


Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.