1.1.5 Interfaces Used by the Declaration API

Like any other API, the protocols declaration API has certain
expectations regarding its parameters. These expectations are documented and
referenced in code using interfaces defined in the protocols.interfaces
module. (The interfaces are also exported directly from the top level of the
protocols package.)

You will rarely use or subclass any of these interface objects, unless you are
customizing or extending the system. Four of the interfaces exist exclusively
for documentation purposes, while the rest are used in adapt() calls
made by the API.

First, let's look at the documentation-only interfaces. It is not necessary
for you to declare that an object supports these interfaces, and the
protocols package never tries to adapt() objects to them.

IAdapterFactory

Up until this point, we've been talking about ``adapters'' rather loosely. The
IAdapterFactory interface formalizes the concept. An adapter
factory is a callable object that accepts an object to be adapted, and returns
an object that provides the protocol on behalf of the passed-in object.
Declaration API functions that take ``adapter'' or ``factory'' arguments must
be adapter factories.

The protocols package supplies two functions that provide
this interface: NO_ADAPTER_NEEDED and DOES_NOT_SUPPORT.
NO_ADAPTER_NEEDED is used to declare that an object provides a
protocol directly, and thus it returns the object passed into it, rather than
some kind of adapter. DOES_NOT_SUPPORT is used to declare that an
object does not support a protocol, even with an adapter. (Since this is the
default case, DOES_NOT_SUPPORT is rarely used, except to indicate
that a subclass does not support an interface that one of its superclasses
does.)

IProtocol

This interface formalizes the idea of a ``protocol object''. As you will
recall from section 1.1.2, a protocol object is any object
that can be used as a Python dictionary key. The second argument to
adapt() must be a protocol object according to this definition.

IAdaptingProtocol

This interface formalizes the idea of an ``adapting protocol'', specifically
that it is a protocol object (i.e., it provides IProtocol) that also
has an __adapt__ method as described in section 1.1.3.
IAdaptingProtocol is a subclass of IProtocol, so of course
adapt() accepts such objects as protocols.

IImplicationListener

This interface is for objects that want to receive notification when new
implication relationships (i.e. adapters) are registered between two protocols.
If you have objects that want to keep track of what interfaces they support,
you may want those object to implement this interface so they can be kept
informed of new protocol-to-protocol adapters.

The other three interfaces are critical to the operation of the declaration API,
and thus must be supported by objects supplied to it. The protocols
package supplies and registers various adapter classes that provide these
interfaces on behalf of many commonly used Python object types. So, for each
interface, we will list ``known supporters'' of that interface, whether they
are classes supplied by protocols, or built-in types that are
automatically adapted to the interface.

We will not, however, go into details here about the methods and behavior
required by each interface. (Those details can be found in section
1.1.9.)

IOpenProtocol

This interface formalizes the ``open protocol'' concept that was introduced
in section 1.1.2. An IOpenProtocol is an
IAdaptingProtocol that can also accept declarations made by the
protocols declaration API.

The protocols package supplies two implementations of this interface:
Protocol and InterfaceClass. Thus, any Interface
subclass or Protocol instance is automatically considered to provide
IOpenProtocol. Note:Interface is an instance of
InterfaceClass, and thus provides IOpenProtocol. But if you
create an instance of an Interface, that object does not provide
IOpenProtocol, because the interfaces provided by an object and its
class (or its instances) can be different.

In addition to its built-in implementations, the protocols package
also supplies and can declare adapter factories that adapt Zope X3 and Twisted's
interface objects to the IOpenProtocol interface, thus allowing
you to use Zope and Twisted interfaces in calls to the declaration API. Similar
adapters for other frameworks' interfaces may be added, if there is sufficient
demand and/or contributed code, and the frameworks' authors do not add the
adapters to their frameworks.

IOpenImplementor

An IOpenImplementor is a class or type that can be told (via the
declaration API) what protocols its instances provide (or support via an
IAdapterFactory). Note that this implies that the instances have
a __conform__ method, or else they would not be able to tell
adapt() about the declared support!

Support for this interface is optional, since types that don't support it
can still have their instances be adapted by IOpenProtocol objects.
The protocols package does not supply any implementations or adapters
for this interface, either. It is intended primarily as a hook for classes
to be able to receive notification about protocol declarations for their
instances.

IOpenProvider

Because objects' behavior usually comes from a class definition, it's not that
often that you will declare that a specific object provides or supports an
interface. But objects like functions and modules do not have a class
definition, and classes themselves sometimes provide an interface. (For
example, one could say that class objects provide an IClass interface.)
So, the declaration API needs to also be able to declare what protocols an
individual object (such as a function, module, or class) supports or provides.

That's what the IOpenProvider interface is for. An
IOpenProvider is an object with a __conform__ method, that can
be told (via the declaration API) what protocols it provides (or supports via
an IAdapterFactory).

Notice that this is different from IOpenImplementor, which deals with
an class or type's instances. IOpenProvider deals with the object
itself. A single object can potentially be both an IOpenProvider and an
IOpenImplementor, if it is a class or type.

The protocols package supplies and declares an adapter factory that
can adapt most Python objects to support this interface, assuming that they
have a __dict__ attribute. Thus, it is acceptable to pass a Python
function, module, or instance of a ``classic'' class to any declaration API
that expects an IOpenProvider argument.

We'll talk more about making protocol declarations for individual objects
(as opposed to types) in section 1.1.6, ``Protocol
Declarations for Individual Objects''.