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.


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.)


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.


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.


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.)


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.


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.


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''.