Interfaces

Introduction

Interfaces define what methods an object provides. Plone extensively uses interfaces to define APIs between different subsystems. They provide a more consistent and declarative way to define bridges between two different things, when duck-typing is not enough.

An Interface defines the shape of a hole where different pieces fit. The shape of the piece is defined by the interface, but the implementation details like color, material, etc. can vary.

See zope.interface package README <http://pypi.python.org/pypi/zope.interface>.

Implementing one or multiple interfaces

Use zope.interface.implements() in your class body. Multiple interfaces can be provided as arguments.

Example:

from zope.interface import implements
from collective.mountpoint.interfaces import ILocalSyncedContent
from ora.objects.interfaces import IORAResearcher

class MyContent(folder.ATFolder):
    """A Researcher synchronized from ORA"""
    implements(IORAResearcher, ILocalSyncedContent)

Removing parent class interface implementations

implementsOnly() redeclares all inherited interface implementations. This is useful if you, for example, want to make z3c.form widget bindings more accurate.

Example:

zope.interface.implementsOnly(IAddressWidget)

Checking whether object provides an interface

providedBy

In Python you can use code:

from yourpackage.interfaces import IMyInterface

if IMyInterface.providedBy(object):
    # do stuff
else:
    # was not the kind of object we wanted

plone_interface_info

In page templates you can use plone_interface_info helper view:

<div tal:define="iinfo context/@@plone_interface_info">
    <span tal:condition="python:iinfo.provides('your.dotted.interface.IName')">
        Do stuff requiring your interface.
    </span>
</div>

See also

Interface resolution order

Interface resolution order (IRO) is the list of interfaces provided by the object (directly, or implemented by a class), sorted by priority.

Interaces are evaluated from zero index (highest priority) to the last index (lowest priority).

You can access this information for the object for debugging purposes using a magical attribute:

object.__provides__.__iro__.

Note

Since adapter factories are dynamic (adapter interfaces not hardcoded on the object), the object can still adapt to interfaces which are not listed in __iro__.

Getting interface string id

The interface id is stored in the __identifier__ attribute.

Example file yourpackage/interfaces.py:

import zope.interface

class IFoo(zope.interface.Interface).
    pass

# id is yourpackage.interfaces.IFoo
id = IFoo.__identifier__

Note that this attribute does not respect import aliasing.

Example:

Products.ATContentTypes.interfaces.IATDocument.__identifier__ is
Products.ATContentTypes.interfaces.document.IATDocument

Getting interface class by its string id

Use the zope.dottedname package.

Example:

import zope.interface
from zope.dottedname.resolve import resolve

class IFoo(zope.interface.Interface).
    pass

# id is yourpackage.interfaces.IFoo
id = IFoo.__identifier__
interface_class == resolve(id)
assert IFoo == interface_class

Applying interfaces for several content types

You can retrofit content types to a marker interface afterwards.

Example use cases:

  • You want to assign a viewlet to a set of particular content types
  • You want to enable certain behavior on certain content types

Note

Retrofitting is needed only when you need to create a common nominator for several otherwise unrelated classes. You can use one existing class or interface as a context without explicitly creating a marker interface. Places accepting zope.interface.Interface as a context usually accept a normal Python class as well (isinstance behavior).

You can assign the marker interface for several classes in ZCML using a <class> declaration:

<!-- List of content types where last modified viewlet is enabled -->
<class class="Products.ATContentTypes.content.document.ATDocument">
   <implements interface=".interfaces.ILastModifiedSupport" />
</class>

<class class="Products.ATContentTypes.content.event.ATEvent">
   <implements interface=".interfaces.ILastModifiedSupport" />
</class>

<class class="Products.ATContentTypes.content.newsitem.ATNewsItem">
   <implements interface=".interfaces.ILastModifiedSupport" />
</class>

Then we can have a viewlet for these content types only using the following (grok example):

from five import grok
from interfaces import ILastModifiedSupport
from plone.app.layout.viewlets.interfaces import IBelowContent

class LastModified(grok.Viewlet):
    """ Viewlet to show the document last modification time.

    This is enabled on Page, Event and News Item wich implement ILastModofiedSupport marker interface.
    """

    grok.context(ILastModifiedSupport)
    grok.viewletmanager(IBelowContent)

Related:

  • zope.dottedname allows you to resolve dotted names to Python objects manually

Dynamic marker interfaces

Zope allows to you to dynamically turn on and off interfaces on any content objects through the ZMI. Browse to any object and visit the Interfaces tab.

Marker interfaces might need to be explicitly declared using the ZCML <interface> directive, so that Zope finds them:

<!-- Declare marker interface, so that it is available in ZMI -->
<interface interface="mfabrik.app.interfaces.promotion.IPromotionsPage" />

Note

Interface dotted name must be directly to the interface class and not an import from other module, like __init__.py.

Setting dynamic marker interfaces programmatically

Use the mark() function from Products.Five.

Example:

from Products.Five.utilities.marker import mark

mark(portal.doc, interfaces.IBuyableMarker)

Note

This marking persists with the object, and is not temporary. Under-the-hood:

mark() delegates to zope.interface.directlyProvides() — with the result that a persistent object (e.g. content item) has a reference to the interface class you mark it with in its __provides__ attribute; this attribute is serialized and loaded by ZODB like any other reference to a class, and zope.interface uses object specification descriptor magic (just like it does for any other object, persistent or not) to resolve provided interfaces.

To remove a marker interface on an object, use the erase() function from Products.Five.

Example:

from Products.Five.utilities.marker import erase

erase(portal.doc, interfaces.IBuyableMarker)

Tagged values

Tagged values are arbitary metadata you can stick on zope.interface.Interface subclasses. For example, the plone.autoform package uses them to set form widget hints for zope.schema data model declarations.




Edit this document

The source code of this file is hosted on GitHub. Everyone can update and fix errors in this document with few clicks - no downloads needed.

  1. Go to Interfaces on GitHub.
  2. Press Fork and edit this file button.
  3. Edit file contents using GitHub's text editor in your web browserm
  4. Fill in the Commit message text box at the end of the page telling why you did the changes. Press Propose file change button next to it when done.
  5. On Send a pull request page you don't need to fill in text anymore. Just press Send pull request button.
  6. Your changes are now queued for review under project's Pull requests tab on Github.

For basic information about updating this manual and Sphinx format please see Writing and updating the manual guide.