[Python-checkins] python/nondist/peps pep-0310.txt,NONE,1.1

goodger@users.sourceforge.net goodger@users.sourceforge.net
Mon, 10 Feb 2003 06:52:53 -0800


Update of /cvsroot/python/python/nondist/peps
In directory sc8-pr-cvs1:/tmp/cvs-serv8313

Added Files:
	pep-0310.txt 
Log Message:
Reliable Acquisition/Release Pairs, by Michael Hudson and Paul Moore

--- NEW FILE: pep-0310.txt ---
PEP: 310
Title: Syntax for Reliable Acquisition/Release Pairs
Version: $Revision: 1.1 $
Last-Modified: $Date: 2003/02/10 14:52:51 $
Author: Michael Hudson <mwh@python.net>,
        Paul Moore <gustav@morpheus.demon.co.uk>
Status: Draft
Type: Standards Track
Content-Type: text/plain
Created: 18-Dec-2002
Python-Version: 2.4
Post-History:


Abstract

    It would be nice to have a less typing-intense way of writing:

        the_lock.acquire()
        try:
            ....
        finally:
            the_lock.release()

    This PEP proposes a piece of syntax (a 'with' block) and a
    "small-i" interface that generalizes the above.


Rationale

    One of the advantages of Python's exception handling philosophy is
    that it makes it harder to do the "wrong" thing (e.g. failing to
    check the return value of some system call).  Currently, this does
    not apply to resource cleanup.  The current syntax for acquisition
    and release of a resource (for example, a lock) is

        the_lock.acquire()
        try:
            ....
        finally:
            the_lock.release()

    This syntax separates the acquisition and release by a (possibly
    large) block of code, which makes it difficult to confirm "at a
    glance" that the code manages the resource correctly.  Another
    common error is to code the "acquire" call within the try block,
    which incorrectly releases the lock if the acquire fails.


Basic Syntax and Semantics

    The syntax of a 'with' statement is as follows::

	'with' [ var '=' ] expr ':'
	    suite

    This statement is defined as being equivalent to the following
    sequence of statements:

	var = expr

	if hasattr(var, "__enter__"):
	    var.__enter__()

	try:
	    suite

	finally:
	    if hasattr(var, "__exit__"):
		var.__exit__()

    If the variable is omitted, an unnamed object is allocated on the
    stack.  In that case, the suite has no access to the unnamed object.


Possible Extensions

    A number of potential extensions to the basic syntax have been
    discussed on the Python Developers list.  None of these extensions
    are included in the solution proposed by this PEP.  In many cases,
    the arguments are nearly equally strong in both directions.  In
    such cases, the PEP has always chosen simplicity, simply because
    where extra power is needed, the existing try block is available.

    Multiple expressions

    One proposal was for allowing multiple expressions within one
    'with' statement.  The __enter__ methods would be called left to
    right, and the __exit__ methods right to left.  The advantage of
    doing so is that where more than one resource is being managed,
    nested 'with' statements will result in code drifting towards the
    right margin.  The solution to this problem is the same as for any
    other deep nesting - factor out some of the code into a separate
    function.  Furthermore, the question of what happens if one of the
    __exit__ methods raises an exception (should the other __exit__
    methods be called?) needs to be addressed.

    Exception handling

    An extension to the protocol to include an optional __except__
    handler, which is called when an exception is raised, and which
    can handle or re-raise the exception, has been suggested.  It is
    not at all clear that the semantics of this extension can be made
    precise and understandable.  For example, should the equivalent
    code be try ... except ... else if an exception handler is
    defined, and try ... finally if not?  How can this be determined
    at compile time, in general?  The alternative is to define the
    code as expanding to a try ... except inside a try ... finally.
    But this may not do the right thing in real life.

    The only use case identified for exception handling is with
    transactional processing (commit on a clean finish, and rollback
    on an exception).  This is probably just as easy to handle with a
    conventional try ... except ... else block, and so the PEP does
    not include any support for exception handlers.


Implementation Notes

    The optional assignment in

        'with' [ var '=' ] expr ':'

    was initially considered to be too hard to parse correctly.
    However, by parsing the statement as

        'with' expr [ '=' expr ] ':'

    and interpreting the result in the compiler phase, this can be
    worked around.

    There is a potential race condition in the code specified as
    equivalent to the with statement.  For example, if a
    KeyboardInterrupt exception is raised between the completion of
    the __enter__ method call and the start of the try block, the
    __exit__ method will not be called.  This can lead to resource
    leaks, or to deadlocks.  [XXX Guido has stated that he cares about
    this sort of race condition, and intends to write some C magic to
    handle them.  The implementation of the 'with' statement should
    copy this.]


Open Issues

    Should existing classes (for example, file-like objects and locks)
    gain appropriate __enter__ and __exit__ methods?  The obvious
    reason in favour is convenience (no adapter needed).  The argument
    against is that if built-in files have this but (say) StringIO
    does not, then code that uses "with" on a file object can't be
    reused with a StringIO object.  So __exit__ = close becomes a part
    of the "file-like object" protocol, which user-defined classes may
    need to support.

    The __enter__ hook may be unnecessary - for many use cases, an
    adapter class is needed and in that case, the work done by the
    __enter__ hook can just as easily be done in the __init__ hook.

    If a way of controlling object lifetimes explicitly was available,
    the function of the __exit__ hook could be taken over by the
    existing __del__ hook.  Unfortunately, no workable proposal for
    controlling object lifetimes has been made so far.


Alternative Ideas

    IEXEC: Holger Krekel -- generalised approach with XML-like syntax
                            (no URL found...)


Backwards Compatibility

    This PEP proposes a new keyword, so the __future__ game will need
    to be played.


References

    There are various python-list and python-dev discussions that
    could be mentioned here.


Copyright

    This document has been placed in the public domain.



Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End: