Interfaces define what methods object provides. Plone extensively uses interfaces to define APIs between different subsystems. They provide more consistent and declarative way to define bridges between two different things, when duck-typing is not enough.
Interface defines the shape of a hole where you can drop different pieces. The shape of the piece is defined by interface, but the implementation details like color, material, etc. can vary.
See zope.interface package README <http://pypi.python.org/pypi/zope.interface>.
Use zope.interface.implements() in your class body. Multiple interfaces can be separated as an argument list.
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)
In Python you can use code:
from yourpackage.interfaces import IMyInterface
if IMyInterface.providedBy(object):
# do stuff
else:
# was not kind of the object we wanted
In page templates you can use plone_interface_info helper view:
<div tal:define="iinfo context/@@plone_interface_info">
<span tal:condition="pytohn:iinfo.provides('your.dotted.interface.IName')">
Do stuff for
</span>
</div>
See also
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
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
You can retrofit content types with a marker interface afterwards.
Example use cases
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 <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
Zope allows to you to dynamically turn on and off interfaces on any content objects through ZMI. Choose any object and Interfaces tab.
Marker interfaces might need to be explicitly declared using 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.