Permalink
Cannot retrieve contributors at this time
527 lines (386 sloc)
17.5 KB
| PEP: 245 | |
| Title: Python Interface Syntax | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: Michel Pelletier <michel@users.sourceforge.net> | |
| Discussions-To: http://www.zope.org/Wikis/Interfaces | |
| Status: Rejected | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 11-Jan-2001 | |
| Python-Version: 2.2 | |
| Post-History: 21-Mar-2001 | |
| 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 [3]_ and describes an underlying framework | |
| which PEP 233 [4]_ 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 [5]_. | |
| 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 [8]_. 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 [6]_, and the new warning | |
| framework [7]_ 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 | |
| ========== | |
| .. [1] https://mail.python.org/pipermail/types-sig/1998-December/date.html | |
| .. [2] http://www.zope.org | |
| .. [3] PEP 232, Function Attributes, Warsaw | |
| http://www.python.org/dev/peps/pep-0232/ | |
| .. [4] PEP 233, Python Online Help, Prescod | |
| http://www.python.org/dev/peps/pep-0233/ | |
| .. [5] http://www.lemburg.com/files/python/mxProxy.html | |
| .. [6] PEP 236, Back to the __future__, Peters | |
| http://www.python.org/dev/peps/pep-0236/ | |
| .. [7] PEP 230, Warning Framework, van Rossum | |
| http://www.python.org/dev/peps/pep-0236/ | |
| .. [8] Python Reference Manual | |
| http://docs.python.org/reference/ | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| End: |