updates from Terence Way's website; editorial corrections

This commit is contained in:
David Goodger 2003-06-09 04:34:03 +00:00
parent c12fe8dba1
commit 90a5162be6
1 changed files with 39 additions and 20 deletions

View File

@ -3,7 +3,7 @@ Title: Programming by Contract for Python
Version: $Revision$
Last-Modified: $Date$
Author: Terence Way <terry@wayforward.net>
Status: Draft
Status: Deferred
Type: Standards Track
Content-Type: text/x-rst
Created: 02-May-2003
@ -22,11 +22,10 @@ Programming contracts extends the language to include invariant
expressions for classes and modules, and pre- and post-condition
expressions for functions and methods.
These expressions (contracts) are similar to assertions: they must
be true or the program is stopped, and run-time checking of the
contracts is typically only enabled while debugging. Contracts
are higher-level than straight assertions and are typically
included in documentation.
These expressions (contracts) are similar to assertions: they must be
true or the program is stopped, and run-time checking of the contracts
is typically only enabled while debugging. Contracts are higher-level
than straight assertions and are typically included in documentation.
Motivation
@ -54,12 +53,14 @@ randomly.
So why add this to the language? Why not have several different
implementations, or let programmers implement their own assertions?
The answer is the behavior of pre-conditions under inheritance.
The answer is the behavior of contracts under inheritance.
If Alice produces a class library protected by her own assertions
package, Bob cannot derive classes from Alice's library and weaken the
pre-conditions, unless both have agreed on an assertions system that
supports weakening pre-conditions. The natural place to find this
Suppose Alice and Bob use different assertions packages. If Alice
produces a class library protected by assertions, Bob cannot derive
classes from Alice's library and expect proper checking of
post-conditions and invariants. If they both use the same assertions
package, then Bob can override Alice's methods yet still test against
Alice's contract assertions. The natural place to find this
assertions system is in the language's run-time library.
@ -81,6 +82,7 @@ Some examples::
START, CONNECTING, CONNECTED, CLOSING, CLOSED = range(5)
class conn:
"""A network connection
inv: self.state in [START, CLOSED, # closed states
@ -91,6 +93,7 @@ Some examples::
"""
class circbuf:
"""A circular buffer.
inv:
@ -129,6 +132,7 @@ variables that the function/method is allowed to modify.
An example::
class circbuf:
def __init__(self, leng):
"""Construct an empty circular buffer.
@ -138,6 +142,13 @@ An example::
len(self.buf) == leng
"""
A double-colon (::) can be used instead of a single colon (:) to
support docstrings written using reStructuredText [#rst]_. For
example, the following two docstrings describe the same contract::
"""pre: leng > 0"""
"""pre:: leng > 0"""
Expressions in pre- and post-conditions are defined in the module
namespace -- they have access to nearly all the variables that the
function can access, except closure variables.
@ -151,6 +162,7 @@ function or method.
An example::
class circbuf:
def get(self):
"""Pull an entry from a non-empty circular buffer.
@ -240,6 +252,10 @@ The class hierarchy::
PreconditionViolationError
PostconditionViolationError
InvariantViolationError
InvalidPreconditionError
The ``InvalidPreconditionError`` is raised when pre-conditions are
illegally strengthened, see the next section on Inheritance.
Example::
@ -261,15 +277,16 @@ A method's post-conditions also include all overridden post-conditions
(method post-conditions are ANDed with all overridden method
post-conditions).
A method's pre-conditions can be ignored if an overridden method's
pre-conditions are met (method pre-conditions are ORed with all
overridden method pre-conditions). This prevents derived classes from
breaking assumptions made by clients that only know the base method's
pre-conditions.
An overridden method's pre-conditions can be ignored if the overriding
method's pre-conditions are met. However, if the overriding method's
pre-conditions fail, *all* of the overridden method's pre-conditions
must also fail. If not, a separate exception is raised, the
InvalidPreconditionError. This supports weakening pre-conditions.
A somewhat contrived example::
class SimpleMailClient:
def send(self, msg, dest):
"""Sends a message to a destination:
@ -305,8 +322,8 @@ A somewhat contrived example::
post: isinstance(__return__, Message)
"""
Because pre-conditions are ORed, a ``ComplexMailClient`` can replace a
``SimpleMailClient`` with no fear of breaking existing code.
Because pre-conditions can only be weakened, a ``ComplexMailClient`` can
replace a ``SimpleMailClient`` with no fear of breaking existing code.
Rationale
@ -374,6 +391,9 @@ or use ``__metaclass__`` [#pydbc]_.
References
==========
.. [#imp] Implementation described in this document.
(http://www.wayforward.net/pycontract/)
.. [#dbc] Design By Contract is a registered trademark of Eiffel
Software Inc.
(http://archive.eiffel.com/doc/manuals/technology/contract/)
@ -393,8 +413,7 @@ References
Daniel Arbuckle
(http://www.nongnu.org/pydbc/)
.. [#imp] Implementation described in this document.
(http://www.wayforward.net/pycontract/)
.. [#rst] ReStructuredText (http://docutils.sourceforge.net/rst.html)
.. [#xp] Extreme Programming Explained, Kent Beck,
ISBN 0-201-61641-6