PEP: 245 Title: Python Interface Syntax Version: $Revision$
Last-Modified: $Date$ Author: Michel Pelletier
<michel@users.sourceforge.net> Status: Rejected Type: Standards Track
Content-Type: text/x-rst Created: 11-Jan-2001 Python-Version: 2.2
Post-History: 21-Mar-2001

Note

The no-longer-available Zope interfaces wiki page
(https://www.zope.org/Wikis/Interfaces) originally linked here,
containing links to further resources for this PEP, can be found on the
Wayback Machine archive. Also, the Interface-Dev Zope mailing list on
which this PEP was discussed was shut down, but its archives remain
available.

Rejection Notice

I'm rejecting this PEP. It's been five years now. While at some point I
expect that Python will have interfaces, it would be naive to expect it
to resemble the syntax in this PEP. Also, PEP 246 is being rejected in
favor of something completely different; interfaces won't play a role in
adaptation or whatever will replace it. GvR.

Introduction

This PEP describes a proposed syntax for creating interface objects in
Python.

Overview

In addition to thinking about adding a static type system to Python, the
Types-SIG was also charged to devise an interface system for Python. In
December of 1998, Jim Fulton released a prototype interfaces system
based on discussions from the SIG. Many of the issues and background
information on this discussion and prototype can be found in the SIG
archives[1].

Around the end of 2000, Digital Creations began thinking about better
component model designs for Zope[2]. Zope's future component model
relies heavily on interface objects. This led to further development of
Jim's "Scarecrow" interfaces prototype. Starting with version 2.3, Zope
comes with an Interface package as standard software. Zope's Interface
package is used as the reference implementation for this PEP.

The syntax proposed by this PEP relies on syntax enhancements describe
in PEP 232 and describes an underlying framework which PEP 233 could be
based upon. There is some work being done with regard to interface
objects and Proxy objects, so for those optional parts of this PEP you
may want to see[3].

The Problem

Interfaces are important because they solve a number of problems that
arise while developing software:

-   There are many implied interfaces in Python, commonly referred to as
    "protocols". Currently determining those protocols is based on
    implementation introspection, but often that also fails. For
    example, defining __getitem__ implies both a sequence and a mapping
    (the former with sequential, integer keys). There is no way for the
    developer to be explicit about which protocols the object intends to
    implement.
-   Python is limited, from the developer's point of view, by the split
    between types and classes. When types are expected, the consumer
    uses code like 'type(foo) == type("")' to determine if 'foo' is a
    string. When instances of classes are expected, the consumer uses
    'isinstance(foo, MyString)' to determine if 'foo' is an instance of
    the 'MyString' class. There is no unified model for determining if
    an object can be used in a certain, valid way.
-   Python's dynamic typing is very flexible and powerful, but it does
    not have the advantage of static typed languages that provide type
    checking. Static typed languages provide you with much more type
    safety, but are often overly verbose because objects can only be
    generalized by common subclassing and used specifically with casting
    (for example, in Java).

There are also a number of documentation problems that interfaces try to
solve.

-   Developers waste a lot of time looking at the source code of your
    system to figure out how objects work.
-   Developers who are new to your system may misunderstand how your
    objects work, causing, and possibly propagating, usage errors.
-   Because a lack of interfaces means usage is inferred from the
    source, developers may end up using methods and attributes that are
    meant for "internal use only".
-   Code inspection can be hard, and very discouraging to novice
    programmers trying to properly understand code written by gurus.
-   A lot of time is wasted when many people try very hard to understand
    obscurity (like undocumented software). Effort spend up front
    documenting interfaces will save much of this time in the end.

Interfaces try to solve these problems by providing a way for you to
specify a contractual obligation for your object, documentation on how
to use an object, and a built-in mechanism for discovering the contract
and the documentation.

Python has very useful introspection features. It is well known that
this makes exploring concepts in the interactive interpreter easier,
because Python gives you the ability to look at all kinds of information
about the objects: the type, doc strings, instance dictionaries, base
classes, unbound methods and more.

Many of these features are oriented toward introspecting, using and
changing the implementation of software, and one of them ("doc strings")
is oriented toward providing documentation. This proposal describes an
extension to this natural introspection framework that describes an
object's interface.

Overview of the Interface Syntax

For the most part, the syntax of interfaces is very much like the syntax
of classes, but future needs, or needs brought up in discussion, may
define new possibilities for interface syntax.

A formal BNF description of the syntax is givena later in the PEP, for
the purposes of illustration, here is an example of two different
interfaces created with the proposed syntax:

    interface CountFishInterface:
        "Fish counting interface"

        def oneFish():
            "Increments the fish count by one"

        def twoFish():
            "Increments the fish count by two"

        def getFishCount():
            "Returns the fish count"

    interface ColorFishInterface:
        "Fish coloring interface"

        def redFish():
            "Sets the current fish color to red"

        def blueFish():
            "Sets the current fish color to blue"

        def getFishColor():
            "This returns the current fish color"

This code, when evaluated, will create two interfaces called
CountFishInterface and ColorFishInterface. These interfaces are defined
by the interface statement.

The prose documentation for the interfaces and their methods come from
doc strings. The method signature information comes from the signatures
of the def statements. Notice how there is no body for the def
statements. The interface does not implement a service to anything; it
merely describes one. Documentation strings on interfaces and interface
methods are mandatory, a 'pass' statement cannot be provided. The
interface equivalent of a pass statement is an empty doc string.

You can also create interfaces that "extend" other interfaces. Here, you
can see a new type of Interface that extends the CountFishInterface and
ColorFishInterface:

    interface FishMarketInterface(CountFishInterface, ColorFishInterface):
        "This is the documentation for the FishMarketInterface"

        def getFishMonger():
            "Returns the fish monger you can interact with"

        def hireNewFishMonger(name):
            "Hire a new fish monger"

        def buySomeFish(quantity=1):
            "Buy some fish at the market"

The FishMarketInterface extends upon the CountFishInterface and
ColorfishInterface.

Interface Assertion

The next step is to put classes and interfaces together by creating a
concrete Python class that asserts that it implements an interface. Here
is an example FishMarket component that might do this:

    class FishError(Error):
        pass

    class FishMarket implements FishMarketInterface:
        number = 0
        color = None
        monger_name = 'Crusty Barnacles'

        def __init__(self, number, color):
            self.number = number
            self.color = color

        def oneFish(self):
            self.number += 1

        def twoFish(self):
            self.number += 2

        def redFish(self):
            self.color = 'red'

        def blueFish(self):
            self.color = 'blue'

        def getFishCount(self):
            return self.number

        def getFishColor(self):
            return self.color

        def getFishMonger(self):
            return self.monger_name

        def hireNewFishMonger(self, name):
            self.monger_name = name

        def buySomeFish(self, quantity=1):
            if quantity > self.count:
                raise FishError("There's not enough fish")
            self.count -= quantity
            return quantity

This new class, FishMarket defines a concrete class which implements the
FishMarketInterface. The object following the implements statement is
called an "interface assertion". An interface assertion can be either an
interface object, or tuple of interface assertions.

The interface assertion provided in a class statement like this is
stored in the class's __implements__ class attribute. After interpreting
the above example, you would have a class statement that can be examined
like this with an 'implements' built-in function:

    >>> FishMarket
    <class FishMarket at 8140f50>
    >>> FishMarket.__implements__
    (<Interface FishMarketInterface at 81006f0>,)
    >>> f = FishMarket(6, 'red')
    >>> implements(f, FishMarketInterface)
    1
    >>>

A class can realize more than one interface. For example, say you had an
interface called ItemInterface that described how an object worked as an
item in a container object. If you wanted to assert that FishMarket
instances realized the ItemInterface interface as well as the
FishMarketInterface, you can provide an interface assertion that
contained a tuple of interface objects to the FishMarket class:

    class FishMarket implements FishMarketInterface, ItemInterface:
        # ...

Interface assertions can also be used if you want to assert that one
class implements an interface, and all of the interfaces that another
class implements:

    class MyFishMarket implements FishMarketInterface, ItemInterface:
        # ...

    class YourFishMarket implements FooInterface, MyFishMarket.__implements__:
        # ...

This new class YourFishMarket, asserts that it implements the
FooInterface, as well as the interfaces implemented by the MyFishMarket
class.

It's worth going into a little bit more detail about interface
assertions. An interface assertion is either an interface object, or a
tuple of interface assertions. For example:

    FooInterface

    FooInterface, (BarInterface, BobInterface)

    FooInterface, (BarInterface, (BobInterface, MyClass.__implements__))

Are all valid interface assertions. When two interfaces define the same
attributes, the order in which information is preferred in the assertion
is from top-to-bottom, left-to-right.

There are other interface proposals that, in the need for simplicity,
have combined the notion of class and interface to provide simple
interface enforcement. Interface objects have a deferred method that
returns a deferred class that implements this behavior:

    >>> FM = FishMarketInterface.deferred()
    >>> class MyFM(FM): pass

    >>> f = MyFM()
    >>> f.getFishMonger()
    Traceback (innermost last):
      File "<stdin>", line 1, in ?
    Interface.Exceptions.BrokenImplementation:
    An object has failed to implement interface FishMarketInterface

            The getFishMonger attribute was not provided.
    >>>

This provides for a bit of passive interface enforcement by telling you
what you forgot to do to implement that interface.

Formal Interface Syntax

Python syntax is defined in a modified BNF grammar notation described in
the Python Reference Manual[4]. This section describes the proposed
interface syntax using this grammar:

    interfacedef:   "interface" interfacename [extends] ":" suite
    extends:        "(" [expression_list] ")"
    interfacename:  identifier

An interface definition is an executable statement. It first evaluates
the extends list, if present. Each item in the extends list should
evaluate to an interface object.

The interface's suite is then executed in a new execution frame (see the
Python Reference Manual, section 4.1), using a newly created local
namespace and the original global namespace. When the interface's suite
finishes execution, its execution frame is discarded but its local
namespace is saved as interface elements. An interface object is then
created using the extends list for the base interfaces and the saved
interface elements. The interface name is bound to this interface object
in the original local namespace.

This PEP also proposes an extension to Python's 'class' statement:

    classdef:    "class" classname [inheritance] [implements] ":" suite
    implements:  "implements" implist
    implist:     expression-list

    classname,
    inheritance,
    suite,
    expression-list:  see the Python Reference Manual

Before a class' suite is executed, the 'inheritance' and 'implements'
statements are evaluated, if present. The 'inheritance' behavior is
unchanged as defined in Section 7.6 of the Language Reference.

The 'implements', if present, is evaluated after inheritance. This must
evaluate to an interface specification, which is either an interface, or
a tuple of interface specifications. If a valid interface specification
is present, the assertion is assigned to the class object's
'__implements__' attribute, as a tuple.

This PEP does not propose any changes to the syntax of function
definitions or assignments.

Classes and Interfaces

The example interfaces above do not describe any kind of behavior for
their methods, they just describe an interface that a typical FishMarket
object would realize.

You may notice a similarity between interfaces extending from other
interfaces and classes sub-classing from other classes. This is a
similar concept. However it is important to note that interfaces extend
interfaces and classes subclass classes. You cannot extend a class or
subclass an interface. Classes and interfaces are separate.

The purpose of a class is to share the implementation of how an object
works. The purpose of an interface is to document how to work with an
object, not how the object is implemented. It is possible to have
several different classes with very different implementations realize
the same interface.

It's also possible to implement one interface with many classes that mix
in pieces the functionality of the interface or, conversely, it's
possible to have one class implement many interfaces. Because of this,
interfaces and classes should not be confused or intermingled.

Interface-aware built-ins

A useful extension to Python's list of built-in functions in the light
of interface objects would be implements(). This builtin would expect
two arguments, an object and an interface, and return a true value if
the object implements the interface, false otherwise. For example:

    >>> interface FooInterface: pass
    >>> class Foo implements FooInterface: pass
    >>> f = Foo()
    >>> implements(f, FooInterface)
    1

Currently, this functionality exists in the reference implementation as
functions in the Interface package, requiring an "import Interface" to
use it. Its existence as a built-in would be purely for a convenience,
and not necessary for using interfaces, and analogous to isinstance()
for classes.

Backward Compatibility

The proposed interface model does not introduce any backward
compatibility issues in Python. The proposed syntax, however, does.

Any existing code that uses interface as an identifier will break. There
may be other kinds of backwards incompatibility that defining interface
as a new keyword will introduce. This extension to Python's syntax does
not change any existing syntax in any backward incompatible way.

The new from __future__ Python syntax (PEP 236), and the new warning
framework (PEP 230) is ideal for resolving this backward
incompatibility. To use interface syntax now, a developer could use the
statement:

    from __future__ import interfaces

In addition, any code that uses the keyword interface as an identifier
will be issued a warning from Python. After the appropriate period of
time, the interface syntax would become standard, the above import
statement would do nothing, and any identifiers named interface would
raise an exception. This period of time is proposed to be 24 months.

Summary of Proposed Changes to Python

Adding new interface keyword and extending class syntax with implements.

Extending class interface to include __implements__.

Add 'implements(obj, interface)' built-in.

Risks

This PEP proposes adding one new keyword to the Python language,
interface. This will break code.

Open Issues

Goals

Syntax

Architecture

Dissenting Opinion

This PEP has not yet been discussed on python-dev.

References

Copyright

This document has been placed in the public domain.

[1] https://mail.python.org/pipermail/types-sig/1998-December/date.html

[2] http://www.zope.org

[3] http://www.lemburg.com/files/python/mxProxy.html

[4] Python Reference Manual http://docs.python.org/reference/