Convert PEPs 222, 224, 281, 284, 310 (#203)

This commit is contained in:
Mariatta 2017-02-10 11:05:40 -08:00 committed by GitHub
parent 0966f373b6
commit c5881cf2b5
5 changed files with 738 additions and 660 deletions

View File

@ -5,110 +5,123 @@ Last-Modified: $Date$
Author: A.M. Kuchling <amk@amk.ca> Author: A.M. Kuchling <amk@amk.ca>
Status: Deferred Status: Deferred
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst
Created: 18-Aug-2000 Created: 18-Aug-2000
Python-Version: 2.1 Python-Version: 2.1
Post-History: 22-Dec-2000 Post-History: 22-Dec-2000
Abstract Abstract
========
This PEP proposes a set of enhancements to the CGI development This PEP proposes a set of enhancements to the CGI development
facilities in the Python standard library. Enhancements might be facilities in the Python standard library. Enhancements might be
new features, new modules for tasks such as cookie support, or new features, new modules for tasks such as cookie support, or
removal of obsolete code. removal of obsolete code.
The original intent was to make improvements to Python 2.1.
However, there seemed little interest from the Python community,
and time was lacking, so this PEP has been deferred to some future
Python release.
The original intent was to make improvements to Python 2.1.
However, there seemed little interest from the Python community,
and time was lacking, so this PEP has been deferred to some future
Python release.
Open Issues Open Issues
===========
This section lists changes that have been suggested, but about This section lists changes that have been suggested, but about
which no firm decision has yet been made. In the final version of which no firm decision has yet been made. In the final version of
this PEP, this section should be empty, as all the changes should this PEP, this section should be empty, as all the changes should
be classified as accepted or rejected. be classified as accepted or rejected.
cgi.py: We should not be told to create our own subclass just so cgi.py: We should not be told to create our own subclass just so
we can handle file uploads. As a practical matter, I have yet to we can handle file uploads. As a practical matter, I have yet to
find the time to do this right, so I end up reading cgi.py's temp find the time to do this right, so I end up reading cgi.py's temp
file into, at best, another file. Some of our legacy code actually file into, at best, another file. Some of our legacy code actually
reads it into a second temp file, then into a final destination! reads it into a second temp file, then into a final destination!
And even if we did, that would mean creating yet another object And even if we did, that would mean creating yet another object
with its __init__ call and associated overhead. with its ``__init__`` call and associated overhead.
cgi.py: Currently, query data with no `=' are ignored. Even if cgi.py: Currently, query data with no ``=`` are ignored. Even if
keep_blank_values is set, queries like `...?value=&...' are keep_blank_values is set, queries like ``...?value=&...`` are
returned with blank values but queries like `...?value&...' are returned with blank values but queries like ``...?value&...`` are
completely lost. It would be great if such data were made completely lost. It would be great if such data were made
available through the FieldStorage interface, either as entries available through the ``FieldStorage`` interface, either as entries
with None as values, or in a separate list. with None as values, or in a separate list.
Utility function: build a query string from a list of 2-tuples Utility function: build a query string from a list of 2-tuples
Dictionary-related utility classes: ``NoKeyErrors`` (returns an empty
string, never a ``KeyError``), ``PartialStringSubstitution`` (returns
the original key string, never a ``KeyError``)
Dictionary-related utility classes: NoKeyErrors (returns an empty
string, never a KeyError), PartialStringSubstitution (returns
the original key string, never a KeyError)
New Modules New Modules
===========
This section lists details about entire new packages or modules This section lists details about entire new packages or modules
that should be added to the Python standard library. that should be added to the Python standard library.
* fcgi.py : A new module adding support for the FastCGI protocol.
Robin Dunn's code needs to be ported to Windows, though.
* fcgi.py : A new module adding support for the FastCGI protocol.
Robin Dunn's code needs to be ported to Windows, though.
Major Changes to Existing Modules Major Changes to Existing Modules
=================================
This section lists details of major changes to existing modules, This section lists details of major changes to existing modules,
whether in implementation or in interface. The changes in this whether in implementation or in interface. The changes in this
section therefore carry greater degrees of risk, either in section therefore carry greater degrees of risk, either in
introducing bugs or a backward incompatibility. introducing bugs or a backward incompatibility.
The cgi.py module would be deprecated. (XXX A new module or
package name hasn't been chosen yet: 'web'? 'cgilib'?)
The cgi.py module would be deprecated. (XXX A new module or
package name hasn't been chosen yet: 'web'? 'cgilib'?)
Minor Changes to Existing Modules Minor Changes to Existing Modules
=================================
This section lists details of minor changes to existing modules. This section lists details of minor changes to existing modules.
These changes should have relatively small implementations, and These changes should have relatively small implementations, and
have little risk of introducing incompatibilities with previous have little risk of introducing incompatibilities with previous
versions. versions.
Rejected Changes Rejected Changes
================
The changes listed in this section were proposed for Python 2.1, The changes listed in this section were proposed for Python 2.1,
but were rejected as unsuitable. For each rejected change, a but were rejected as unsuitable. For each rejected change, a
rationale is given describing why the change was deemed rationale is given describing why the change was deemed
inappropriate. inappropriate.
* An HTML generation module is not part of this PEP. Several such * An HTML generation module is not part of this PEP. Several such
modules exist, ranging from HTMLgen's purely programming modules exist, ranging from HTMLgen's purely programming
interface to ASP-inspired simple templating to DTML's complex interface to ASP-inspired simple templating to DTML's complex
templating. There's no indication of which templating module to templating. There's no indication of which templating module to
enshrine in the standard library, and that probably means that enshrine in the standard library, and that probably means that
no module should be so chosen. no module should be so chosen.
* cgi.py: Allowing a combination of query data and POST data.
This doesn't seem to be standard at all, and therefore is
dubious practice.
* cgi.py: Allowing a combination of query data and POST data.
This doesn't seem to be standard at all, and therefore is
dubious practice.
Proposed Interface Proposed Interface
==================
XXX open issues: naming convention (studlycaps or XXX open issues: naming convention (studlycaps or
underline-separated?); need to look at the cgi.parse*() functions underline-separated?); need to look at the ``cgi.parse*()`` functions
and see if they can be simplified, too. and see if they can be simplified, too.
Parsing functions: carry over most of the ``parse*`` functions from
cgi.py
::
Parsing functions: carry over most of the parse* functions from
cgi.py
# The Response class borrows most of its methods from Zope's # The Response class borrows most of its methods from Zope's
# HTTPResponse class. # HTTPResponse class.
class Response: class Response:
""" """
Attributes: Attributes:
@ -116,57 +129,57 @@ Proposed Interface
headers: dictionary of response headers headers: dictionary of response headers
body: string containing the body of the HTTP response body: string containing the body of the HTTP response
""" """
def __init__(self, status=200, headers={}, body=""): def __init__(self, status=200, headers={}, body=""):
pass pass
def setStatus(self, status, reason=None): def setStatus(self, status, reason=None):
"Set the numeric HTTP response code" "Set the numeric HTTP response code"
pass pass
def setHeader(self, name, value): def setHeader(self, name, value):
"Set an HTTP header" "Set an HTTP header"
pass pass
def setBody(self, body): def setBody(self, body):
"Set the body of the response" "Set the body of the response"
pass pass
def setCookie(self, name, value, def setCookie(self, name, value,
path = '/', path = '/',
comment = None, comment = None,
domain = None, domain = None,
max-age = None, max-age = None,
expires = None, expires = None,
secure = 0 secure = 0
): ):
"Set a cookie" "Set a cookie"
pass pass
def expireCookie(self, name): def expireCookie(self, name):
"Remove a cookie from the user" "Remove a cookie from the user"
pass pass
def redirect(self, url): def redirect(self, url):
"Redirect the browser to another URL" "Redirect the browser to another URL"
pass pass
def __str__(self): def __str__(self):
"Convert entire response to a string" "Convert entire response to a string"
pass pass
def dump(self): def dump(self):
"Return a string representation useful for debugging" "Return a string representation useful for debugging"
pass pass
# XXX methods for specific classes of error:serverError, # XXX methods for specific classes of error:serverError,
# badRequest, etc.? # badRequest, etc.?
class Request: class Request:
""" """
Attributes: Attributes:
XXX should these be dictionaries, or dictionary-like objects? XXX should these be dictionaries, or dictionary-like objects?
.headers : dictionary containing HTTP headers .headers : dictionary containing HTTP headers
@ -174,31 +187,31 @@ Proposed Interface
.fields : data from the form .fields : data from the form
.env : environment dictionary .env : environment dictionary
""" """
def __init__(self, environ=os.environ, stdin=sys.stdin, def __init__(self, environ=os.environ, stdin=sys.stdin,
keep_blank_values=1, strict_parsing=0): keep_blank_values=1, strict_parsing=0):
"""Initialize the request object, using the provided environment """Initialize the request object, using the provided environment
and standard input.""" and standard input."""
pass pass
# Should people just use the dictionaries directly? # Should people just use the dictionaries directly?
def getHeader(self, name, default=None): def getHeader(self, name, default=None):
pass pass
def getCookie(self, name, default=None): def getCookie(self, name, default=None):
pass pass
def getField(self, name, default=None): def getField(self, name, default=None):
"Return field's value as a string (even if it's an uploaded file)" "Return field's value as a string (even if it's an uploaded file)"
pass pass
def getUploadedFile(self, name): def getUploadedFile(self, name):
"""Returns a file object that can be read to obtain the contents """Returns a file object that can be read to obtain the contents
of an uploaded file. XXX should this report an error if the of an uploaded file. XXX should this report an error if the
field isn't actually an uploaded file? Or should it wrap field isn't actually an uploaded file? Or should it wrap
a StringIO around simple fields for consistency? a StringIO around simple fields for consistency?
""" """
def getURL(self, n=0, query_string=0): def getURL(self, n=0, query_string=0):
"""Return the URL of the current request, chopping off 'n' path """Return the URL of the current request, chopping off 'n' path
components from the right. Eg. if the URL is components from the right. Eg. if the URL is
@ -209,48 +222,50 @@ Proposed Interface
def getBaseURL(self, n=0): def getBaseURL(self, n=0):
"""Return the base URL of the current request, adding 'n' path """Return the base URL of the current request, adding 'n' path
components to the end to recreate more of the whole URL. components to the end to recreate more of the whole URL.
Eg. if the request URL is Eg. if the request URL is
"http://foo.com/q/bar/baz/qux", n=0 would return "http://foo.com/q/bar/baz/qux", n=0 would return
"http://foo.com/", and n=2 "http://foo.com/q/bar". "http://foo.com/", and n=2 "http://foo.com/q/bar".
Returned URL does not include the query string, if any. Returned URL does not include the query string, if any.
""" """
def dump(self): def dump(self):
"String representation suitable for debugging output" "String representation suitable for debugging output"
pass pass
# Possibilities? I don't know if these are worth doing in the # Possibilities? I don't know if these are worth doing in the
# basic objects. # basic objects.
def getBrowser(self): def getBrowser(self):
"Returns Mozilla/IE/Lynx/Opera/whatever" "Returns Mozilla/IE/Lynx/Opera/whatever"
def isSecure(self): def isSecure(self):
"Return true if this is an SSLified request" "Return true if this is an SSLified request"
# Module-level function
# Module-level function
def wrapper(func, logfile=sys.stderr): def wrapper(func, logfile=sys.stderr):
""" """
Calls the function 'func', passing it the arguments Calls the function 'func', passing it the arguments
(request, response, logfile). Exceptions are trapped and (request, response, logfile). Exceptions are trapped and
sent to the file 'logfile'. sent to the file 'logfile'.
""" """
# This wrapper will detect if it's being called from the command-line, # This wrapper will detect if it's being called from the command-line,
# and if so, it will run in a debugging mode; name=value pairs # and if so, it will run in a debugging mode; name=value pairs
# can be entered on standard input to set field values. # can be entered on standard input to set field values.
# (XXX how to do file uploads in this syntax?) # (XXX how to do file uploads in this syntax?)
Copyright Copyright
=========
This document has been placed in the public domain.
This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
End: indent-tabs-mode: nil
End:

View File

@ -5,253 +5,272 @@ Last-Modified: $Date$
Author: mal@lemburg.com (Marc-André Lemburg) Author: mal@lemburg.com (Marc-André Lemburg)
Status: Rejected Status: Rejected
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst
Created: 23-Aug-2000 Created: 23-Aug-2000
Python-Version: 2.1 Python-Version: 2.1
Post-History: Post-History:
Introduction Introduction
============
This PEP describes the "attribute docstring" proposal for Python This PEP describes the "attribute docstring" proposal for Python
2.0. This PEP tracks the status and ownership of this feature. 2.0. This PEP tracks the status and ownership of this feature.
It contains a description of the feature and outlines changes It contains a description of the feature and outlines changes
necessary to support the feature. The CVS revision history of necessary to support the feature. The CVS revision history of
this file contains the definitive historical record. this file contains the definitive historical record.
Rationale Rationale
=========
This PEP proposes a small addition to the way Python currently This PEP proposes a small addition to the way Python currently
handles docstrings embedded in Python code. handles docstrings embedded in Python code.
Python currently only handles the case of docstrings which appear Python currently only handles the case of docstrings which appear
directly after a class definition, a function definition or as directly after a class definition, a function definition or as
first string literal in a module. The string literals are added first string literal in a module. The string literals are added
to the objects in question under the __doc__ attribute and are to the objects in question under the ``__doc__`` attribute and are
from then on available for introspection tools which can extract from then on available for introspection tools which can extract
the contained information for help, debugging and documentation the contained information for help, debugging and documentation
purposes. purposes.
Docstrings appearing in locations other than the ones mentioned Docstrings appearing in locations other than the ones mentioned
are simply ignored and don't result in any code generation. are simply ignored and don't result in any code generation.
Here is an example: Here is an example::
class C: class C:
"class C doc-string" "class C doc-string"
a = 1 a = 1
"attribute C.a doc-string (1)" "attribute C.a doc-string (1)"
b = 2 b = 2
"attribute C.b doc-string (2)" "attribute C.b doc-string (2)"
The docstrings (1) and (2) are currently being ignored by the The docstrings (1) and (2) are currently being ignored by the
Python byte code compiler, but could obviously be put to good use Python byte code compiler, but could obviously be put to good use
for documenting the named assignments that precede them. for documenting the named assignments that precede them.
This PEP proposes to also make use of these cases by proposing
semantics for adding their content to the objects in which they
appear under new generated attribute names.
The original idea behind this approach which also inspired the This PEP proposes to also make use of these cases by proposing
above example was to enable inline documentation of class semantics for adding their content to the objects in which they
attributes, which can currently only be documented in the class's appear under new generated attribute names.
docstring or using comments which are not available for
introspection. The original idea behind this approach which also inspired the
above example was to enable inline documentation of class
attributes, which can currently only be documented in the class's
docstring or using comments which are not available for
introspection.
Implementation Implementation
==============
Docstrings are handled by the byte code compiler as expressions. Docstrings are handled by the byte code compiler as expressions.
The current implementation special cases the few locations The current implementation special cases the few locations
mentioned above to make use of these expressions, but otherwise mentioned above to make use of these expressions, but otherwise
ignores the strings completely. ignores the strings completely.
To enable use of these docstrings for documenting named To enable use of these docstrings for documenting named
assignments (which is the natural way of defining e.g. class assignments (which is the natural way of defining e.g. class
attributes), the compiler will have to keep track of the last attributes), the compiler will have to keep track of the last
assigned name and then use this name to assign the content of the assigned name and then use this name to assign the content of the
docstring to an attribute of the containing object by means of docstring to an attribute of the containing object by means of
storing it in as a constant which is then added to the object's storing it in as a constant which is then added to the object's
namespace during object construction time. namespace during object construction time.
In order to preserve features like inheritance and hiding of In order to preserve features like inheritance and hiding of
Python's special attributes (ones with leading and trailing double Python's special attributes (ones with leading and trailing double
underscores), a special name mangling has to be applied which underscores), a special name mangling has to be applied which
uniquely identifies the docstring as belonging to the name uniquely identifies the docstring as belonging to the name
assignment and allows finding the docstring later on by inspecting assignment and allows finding the docstring later on by inspecting
the namespace. the namespace.
The following name mangling scheme achieves all of the above: The following name mangling scheme achieves all of the above::
__doc_<attributename>__ __doc_<attributename>__
To keep track of the last assigned name, the byte code compiler To keep track of the last assigned name, the byte code compiler
stores this name in a variable of the compiling structure. This stores this name in a variable of the compiling structure. This
variable defaults to NULL. When it sees a docstring, it then variable defaults to NULL. When it sees a docstring, it then
checks the variable and uses the name as basis for the above name checks the variable and uses the name as basis for the above name
mangling to produce an implicit assignment of the docstring to the mangling to produce an implicit assignment of the docstring to the
mangled name. It then resets the variable to NULL to avoid mangled name. It then resets the variable to NULL to avoid
duplicate assignments. duplicate assignments.
If the variable does not point to a name (i.e. is NULL), no If the variable does not point to a name (i.e. is NULL), no
assignments are made. These will continue to be ignored like assignments are made. These will continue to be ignored like
before. All classical docstrings fall under this case, so no before. All classical docstrings fall under this case, so no
duplicate assignments are done. duplicate assignments are done.
In the above example this would result in the following new class In the above example this would result in the following new class
attributes to be created: attributes to be created::
C.__doc_a__ == "attribute C.a doc-string (1)" C.__doc_a__ == "attribute C.a doc-string (1)"
C.__doc_b__ == "attribute C.b doc-string (2)" C.__doc_b__ == "attribute C.b doc-string (2)"
A patch to the current CVS version of Python 2.0 which implements A patch to the current CVS version of Python 2.0 which implements
the above is available on SourceForge at [1]. the above is available on SourceForge at [1]_.
Caveats of the Implementation Caveats of the Implementation
=============================
Since the implementation does not reset the compiling structure
variable when processing a non-expression, e.g. a function
definition, the last assigned name remains active until either the
next assignment or the next occurrence of a docstring.
This can lead to cases where the docstring and assignment may be Since the implementation does not reset the compiling structure
separated by other expressions: variable when processing a non-expression, e.g. a function
definition, the last assigned name remains active until either the
next assignment or the next occurrence of a docstring.
class C: This can lead to cases where the docstring and assignment may be
"C doc string" separated by other expressions::
b = 2 class C:
"C doc string"
def x(self): b = 2
"C.x doc string"
y = 3
return 1
"b's doc string" def x(self):
"C.x doc string"
y = 3
return 1
Since the definition of method "x" currently does not reset the "b's doc string"
used assignment name variable, it is still valid when the compiler
reaches the docstring "b's doc string" and thus assigns the string
to __doc_b__.
A possible solution to this problem would be resetting the name Since the definition of method "x" currently does not reset the
variable for all non-expression nodes in the compiler. used assignment name variable, it is still valid when the compiler
reaches the docstring "b's doc string" and thus assigns the string
to ``__doc_b__``.
A possible solution to this problem would be resetting the name
variable for all non-expression nodes in the compiler.
Possible Problems Possible Problems
=================
Even though highly unlikely, attribute docstrings could get Even though highly unlikely, attribute docstrings could get
accidentally concatenated to the attribute's value: accidentally concatenated to the attribute's value::
class C: class C:
x = "text" \ x = "text" \
"x's docstring" "x's docstring"
The trailing slash would cause the Python compiler to concatenate The trailing slash would cause the Python compiler to concatenate
the attribute value and the docstring. the attribute value and the docstring.
A modern syntax highlighting editor would easily make this A modern syntax highlighting editor would easily make this
accident visible, though, and by simply inserting emtpy lines accident visible, though, and by simply inserting emtpy lines
between the attribute definition and the docstring you can avoid between the attribute definition and the docstring you can avoid
the possible concatenation completely, so the problem is the possible concatenation completely, so the problem is
negligible. negligible.
Another possible problem is that of using triple quoted strings as Another possible problem is that of using triple quoted strings as
a way to uncomment parts of your code. a way to uncomment parts of your code.
If there happens to be an assignment just before the start of the If there happens to be an assignment just before the start of the
comment string, then the compiler will treat the comment as comment string, then the compiler will treat the comment as
docstring attribute and apply the above logic to it. docstring attribute and apply the above logic to it.
Besides generating a docstring for an otherwise undocumented Besides generating a docstring for an otherwise undocumented
attribute there is no breakage. attribute there is no breakage.
Comments from our BDFL Comments from our BDFL
======================
Early comments on the PEP from Guido: Early comments on the PEP from Guido:
I "kinda" like the idea of having attribute docstrings (meaning I "kinda" like the idea of having attribute docstrings (meaning
it's not of great importance to me) but there are two things I it's not of great importance to me) but there are two things I
don't like in your current proposal: don't like in your current proposal:
1. The syntax you propose is too ambiguous: as you say, 1. The syntax you propose is too ambiguous: as you say,
stand-alone string literal are used for other purposes and could stand-alone string literal are used for other purposes and could
suddenly become attribute docstrings. suddenly become attribute docstrings.
2. I don't like the access method either (__doc_<attrname>__). 2. I don't like the access method either (``__doc_<attrname>__``).
The author's reply: The author's reply
::
> 1. The syntax you propose is too ambiguous: as you say, stand-alone > 1. The syntax you propose is too ambiguous: as you say, stand-alone
> string literal are used for other purposes and could suddenly > string literal are used for other purposes and could suddenly
> become attribute docstrings. > become attribute docstrings.
This can be fixed by introducing some extra checks in the
compiler to reset the "doc attribute" flag in the compiler
struct.
> 2. I don't like the access method either (__doc_<attrname>__). This can be fixed by introducing some extra checks in the
compiler to reset the "doc attribute" flag in the compiler
struct.
Any other name will do. It will only have to match these ::
criteria:
* must start with two underscores (to match __doc__) > 2. I don't like the access method either (``__doc_<attrname>__``).
* must be extractable using some form of inspection (e.g. by using
a naming convention which includes some fixed name part)
* must be compatible with class inheritance (i.e. should be
stored as attribute)
Later on in March, Guido pronounced on this PEP in March 2001 (on Any other name will do. It will only have to match these
python-dev). Here are his reasons for rejection mentioned in criteria:
private mail to the author of this PEP:
... * must start with two underscores (to match ``__doc__``)
* must be extractable using some form of inspection (e.g. by using
a naming convention which includes some fixed name part)
* must be compatible with class inheritance (i.e. should be
stored as attribute)
It might be useful, but I really hate the proposed syntax. Later on in March, Guido pronounced on this PEP in March 2001 (on
python-dev). Here are his reasons for rejection mentioned in
private mail to the author of this PEP:
a = 1 ...
"foo bar"
b = 1
I really have no way to know whether "foo bar" is a docstring It might be useful, but I really hate the proposed syntax.
for a or for b.
... ::
You can use this convention:
a = 1 a = 1
__doc_a__ = "doc string for a" "foo bar"
b = 1
This makes it available at runtime. I really have no way to know whether "foo bar" is a docstring
for a or for b.
> Are you completely opposed to adding attribute documentation ...
> to Python or is it just the way the implementation works ? I
> find the syntax proposed in the PEP very intuitive and many You can use this convention::
> other users on c.l.p and in private emails have supported it
> at the time I wrote the PEP. a = 1
__doc_a__ = "doc string for a"
This makes it available at runtime.
::
> Are you completely opposed to adding attribute documentation
> to Python or is it just the way the implementation works ? I
> find the syntax proposed in the PEP very intuitive and many
> other users on c.l.p and in private emails have supported it
> at the time I wrote the PEP.
It's not the implementation, it's the syntax. It doesn't
convey a clear enough coupling between the variable and the
doc string.
It's not the implementation, it's the syntax. It doesn't
convey a clear enough coupling between the variable and the
doc string.
Copyright Copyright
=========
This document has been placed in the Public Domain. This document has been placed in the Public Domain.
References References
==========
[1] http://sourceforge.net/patch/?func=detailpatch&patch_id=101264&group_id=5470 .. [1] http://sourceforge.net/patch/?func=detailpatch&patch_id=101264&group_id=5470
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
End: indent-tabs-mode: nil
End:

View File

@ -5,56 +5,62 @@ Last-Modified: $Date$
Author: magnus@hetland.org (Magnus Lie Hetland) Author: magnus@hetland.org (Magnus Lie Hetland)
Status: Rejected Status: Rejected
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst
Created: 11-Feb-2002 Created: 11-Feb-2002
Python-Version: 2.3 Python-Version: 2.3
Post-History: Post-History:
Abstract Abstract
========
This PEP describes yet another way of exposing the loop counter in
for-loops. It basically proposes that the functionality of the
function ``indices()`` from PEP 212 [1]_ be included in the existing
functions ``range()`` and ``xrange()``.
This PEP describes yet another way of exposing the loop counter in
for-loops. It basically proposes that the functionality of the
function indices() from PEP 212 [1] be included in the existing
functions range() and xrange().
Pronouncement Pronouncement
=============
In commenting on PEP 279's enumerate() function, this PEP's author In commenting on PEP 279's ``enumerate()`` function, this PEP's author
offered, "I'm quite happy to have it make PEP 281 obsolete." offered, "I'm quite happy to have it make PEP 281 obsolete."
Subsequently, PEP 279 was accepted into Python 2.3. Subsequently, PEP 279 was accepted into Python 2.3.
On 17 June 2005, the BDFL concurred with it being obsolete and On 17 June 2005, the BDFL concurred with it being obsolete and
hereby rejected the PEP. For the record, he found some of the hereby rejected the PEP. For the record, he found some of the
examples to somewhat jarring in appearance: examples to somewhat jarring in appearance::
>>> range(range(5), range(10), range(2)) >>> range(range(5), range(10), range(2))
[5, 7, 9] [5, 7, 9]
Motivation Motivation
==========
It is often desirable to loop over the indices of a sequence. PEP It is often desirable to loop over the indices of a sequence. PEP
212 describes several ways of doing this, including adding a 212 describes several ways of doing this, including adding a
built-in function called indices, conceptually defined as built-in function called indices, conceptually defined as::
def indices(sequence): def indices(sequence):
return range(len(sequence)) return range(len(sequence))
On the assumption that adding functionality to an existing built-in On the assumption that adding functionality to an existing built-in
function may be less intrusive than adding a new built-in function, function may be less intrusive than adding a new built-in function,
this PEP proposes adding this functionality to the existing this PEP proposes adding this functionality to the existing
functions range() and xrange(). functions ``range()`` and ``xrange()``.
Specification Specification
=============
It is proposed that all three arguments to the built-in functions It is proposed that all three arguments to the built-in functions
range() and xrange() are allowed to be objects with a length ``range()`` and ``xrange()`` are allowed to be objects with a length
(i.e. objects implementing the __len__ method). If an argument (i.e. objects implementing the ``__len__`` method). If an argument
cannot be interpreted as an integer (i.e. it has no __int__ cannot be interpreted as an integer (i.e. it has no ``__int__``
method), its length will be used instead. method), its length will be used instead.
Examples: Examples::
>>> range(range(10)) >>> range(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
@ -74,18 +80,19 @@ Specification
Alternatives Alternatives
============
A natural alternative to the above specification is allowing A natural alternative to the above specification is allowing
xrange() to access its arguments in a lazy manner. Thus, instead ``xrange()`` to access its arguments in a lazy manner. Thus, instead
of using their length explicitly, xrange can return one index for of using their length explicitly, ``xrange`` can return one index for
each element of the stop argument until the end is reached. A each element of the stop argument until the end is reached. A
similar lazy treatment makes little sense for the start and step similar lazy treatment makes little sense for the start and step
arguments since their length must be calculated before iteration arguments since their length must be calculated before iteration
can begin. (Actually, the length of the step argument isn't needed can begin. (Actually, the length of the step argument isn't needed
until the second element is returned.) until the second element is returned.)
A pseudo-implementation (using only the stop argument, and assuming A pseudo-implementation (using only the stop argument, and assuming
that it is iterable) is: that it is iterable) is::
def xrange(stop): def xrange(stop):
i = 0 i = 0
@ -93,26 +100,26 @@ Alternatives
yield i yield i
i += 1 i += 1
Testing whether to use int() or lazy iteration could be done by Testing whether to use ``int()`` or lazy iteration could be done by
checking for an __iter__ attribute. (This example assumes the checking for an ``__iter__`` attribute. (This example assumes the
presence of generators, but could easily have been implemented as a presence of generators, but could easily have been implemented as a
plain iterator object.) plain iterator object.)
It may be questionable whether this feature is truly useful, since It may be questionable whether this feature is truly useful, since
one would not be able to access the elements of the iterable object one would not be able to access the elements of the iterable object
inside the for loop through indexing. inside the for loop through indexing.
Example: Example::
# Printing the numbers of the lines of a file: # Printing the numbers of the lines of a file:
for num in range(file): for num in range(file):
print num # The line itself is not accessible print num # The line itself is not accessible
A more controversial alternative (to deal with this) would be to A more controversial alternative (to deal with this) would be to
let range() behave like the function irange() of PEP 212 when let ``range()`` behave like the function ``irange()`` of PEP 212 when
supplied with a sequence. supplied with a sequence.
Example: Example::
>>> range(5) >>> range(5)
[0, 1, 2, 3, 4] [0, 1, 2, 3, 4]
@ -121,27 +128,31 @@ Alternatives
Backwards Compatibility Backwards Compatibility
=======================
The proposal could cause backwards incompatibilities if arguments The proposal could cause backwards incompatibilities if arguments
are used which implement both __int__ and __len__ (or __iter__ in are used which implement both ``__int__`` and ``__len__`` (or ``__iter__`` in
the case of lazy iteration with xrange). The author does not the case of lazy iteration with ``xrange``). The author does not
believe that this is a significant problem. believe that this is a significant problem.
References and Footnotes References and Footnotes
========================
[1] PEP 212, Loop Counter Iteration .. [1] PEP 212, Loop Counter Iteration
http://www.python.org/dev/peps/pep-0212/ http://www.python.org/dev/peps/pep-0212/
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
fill-column: 70 indent-tabs-mode: nil
End: fill-column: 70
End:

View File

@ -3,270 +3,285 @@ Title: Integer for-loops
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: David Eppstein <eppstein@ics.uci.edu>, Author: David Eppstein <eppstein@ics.uci.edu>,
Greg Ewing <greg.ewing@canterbury.ac.nz> Greg Ewing <greg.ewing@canterbury.ac.nz>
Status: Rejected Status: Rejected
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst
Created: 1-Mar-2002 Created: 1-Mar-2002
Python-Version: 2.3 Python-Version: 2.3
Post-History: Post-History:
Abstract Abstract
========
This PEP proposes to simplify iteration over intervals of This PEP proposes to simplify iteration over intervals of
integers, by extending the range of expressions allowed after a integers, by extending the range of expressions allowed after a
"for" keyword to allow three-way comparisons such as "for" keyword to allow three-way comparisons such as::
for lower <= var < upper: for lower <= var < upper:
in place of the current in place of the current::
for item in list: for item in list:
syntax. The resulting loop or list iteration will loop over all
values of var that make the comparison true, starting from the
left endpoint of the given interval.
syntax. The resulting loop or list iteration will loop over all
values of var that make the comparison true, starting from the
left endpoint of the given interval.
Pronouncement Pronouncement
=============
This PEP is rejected. There were a number of fixable issues with
the proposal (see the fixups listed in Raymond Hettinger's
python-dev post on 18 June 2005 [5]_). However, even with the fixups the
proposal did not garner support. Specifically, Guido did not buy
the premise that the ``range()`` format needed fixing, "The whole point
(15 years ago) of ``range()`` was to *avoid* needing syntax to specify a
loop over numbers. I think it's worked out well and there's nothing
that needs to be fixed (except ``range()`` needs to become an iterator,
which it will in Python 3.0)."
This PEP is rejected. There were a number of fixable issues with
the proposal (see the fixups listed in Raymond Hettinger's
python-dev post on 18 June 2005). However, even with the fixups the
proposal did not garner support. Specifically, Guido did not buy
the premise that the range() format needed fixing, "The whole point
(15 years ago) of range() was to *avoid* needing syntax to specify a
loop over numbers. I think it's worked out well and there's nothing
that needs to be fixed (except range() needs to become an iterator,
which it will in Python 3.0)."
Rationale Rationale
=========
One of the most common uses of for-loops in Python is to iterate One of the most common uses of for-loops in Python is to iterate
over an interval of integers. Python provides functions range() over an interval of integers. Python provides functions ``range()``
and xrange() to generate lists and iterators for such intervals, and ``xrange()`` to generate lists and iterators for such intervals,
which work best for the most frequent case: half-open intervals which work best for the most frequent case: half-open intervals
increasing from zero. However, the range() syntax is more awkward increasing from zero. However, the ``range()`` syntax is more awkward
for open or closed intervals, and lacks symmetry when reversing for open or closed intervals, and lacks symmetry when reversing
the order of iteration. In addition, the call to an unfamiliar the order of iteration. In addition, the call to an unfamiliar
function makes it difficult for newcomers to Python to understand function makes it difficult for newcomers to Python to understand
code that uses range() or xrange(). code that uses ``range()`` or ``xrange()``.
The perceived lack of a natural, intuitive integer iteration The perceived lack of a natural, intuitive integer iteration
syntax has led to heated debate on python-list, and spawned at syntax has led to heated debate on python-list, and spawned at
least four PEPs before this one. PEP 204 [1] (rejected) proposed least four PEPs before this one. PEP 204 [1]_ (rejected) proposed
to re-use Python's slice syntax for integer ranges, leading to a to re-use Python's slice syntax for integer ranges, leading to a
terser syntax but not solving the readability problem of terser syntax but not solving the readability problem of
multi-argument range(). PEP 212 [2] (deferred) proposed several multi-argument ``range()``. PEP 212 [2]_ (deferred) proposed several
syntaxes for directly converting a list to a sequence of integer syntaxes for directly converting a list to a sequence of integer
indices, in place of the current idiom indices, in place of the current idiom::
range(len(list)) range(len(list))
for such conversion, and PEP 281 [3] proposes to simplify the same for such conversion, and PEP 281 [3]_ proposes to simplify the same
idiom by allowing it to be written as idiom by allowing it to be written as::
range(list). range(list).
PEP 276 [4] proposes to allow automatic conversion of integers to PEP 276 [4]_ proposes to allow automatic conversion of integers to
iterators, simplifying the most common half-open case but not iterators, simplifying the most common half-open case but not
addressing the complexities of other types of interval. addressing the complexities of other types of interval.
Additional alternatives have been discussed on python-list. Additional alternatives have been discussed on python-list.
The solution described here is to allow a three-way comparison The solution described here is to allow a three-way comparison
after a "for" keyword, both in the context of a for-loop and of a after a "for" keyword, both in the context of a for-loop and of a
list comprehension: list comprehension::
for lower <= var < upper: for lower <= var < upper:
This would cause iteration over an interval of consecutive This would cause iteration over an interval of consecutive
integers, beginning at the left bound in the comparison and ending integers, beginning at the left bound in the comparison and ending
at the right bound. The exact comparison operations used would at the right bound. The exact comparison operations used would
determine whether the interval is open or closed at either end and determine whether the interval is open or closed at either end and
whether the integers are considered in ascending or descending whether the integers are considered in ascending or descending
order. order.
This syntax closely matches standard mathematical notation, so is This syntax closely matches standard mathematical notation, so is
likely to be more familiar to Python novices than the current likely to be more familiar to Python novices than the current
range() syntax. Open and closed interval endpoints are equally ``range()`` syntax. Open and closed interval endpoints are equally
easy to express, and the reversal of an integer interval can be easy to express, and the reversal of an integer interval can be
formed simply by swapping the two endpoints and reversing the formed simply by swapping the two endpoints and reversing the
comparisons. In addition, the semantics of such a loop would comparisons. In addition, the semantics of such a loop would
closely resemble one way of interpreting the existing Python closely resemble one way of interpreting the existing Python
for-loops: for-loops::
for item in list for item in list
iterates over exactly those values of item that cause the iterates over exactly those values of item that cause the
expression expression::
item in list item in list
to be true. Similarly, the new format to be true. Similarly, the new format::
for lower <= var < upper: for lower <= var < upper:
would iterate over exactly those integer values of var that cause would iterate over exactly those integer values of var that cause
the expression the expression::
lower <= var < upper lower <= var < upper
to be true. to be true.
Specification Specification
=============
We propose to extend the syntax of a for statement, currently We propose to extend the syntax of a for statement, currently::
for_stmt: "for" target_list "in" expression_list ":" suite for_stmt: "for" target_list "in" expression_list ":" suite
["else" ":" suite] ["else" ":" suite]
as described below: as described below::
for_stmt: "for" for_test ":" suite ["else" ":" suite] for_stmt: "for" for_test ":" suite ["else" ":" suite]
for_test: target_list "in" expression_list | for_test: target_list "in" expression_list |
or_expr less_comp or_expr less_comp or_expr | or_expr less_comp or_expr less_comp or_expr |
or_expr greater_comp or_expr greater_comp or_expr or_expr greater_comp or_expr greater_comp or_expr
less_comp: "<" | "<=" less_comp: "<" | "<="
greater_comp: ">" | ">=" greater_comp: ">" | ">="
Similarly, we propose to extend the syntax of list comprehensions, Similarly, we propose to extend the syntax of list comprehensions,
currently currently::
list_for: "for" expression_list "in" testlist [list_iter] list_for: "for" expression_list "in" testlist [list_iter]
by replacing it with: by replacing it with::
list_for: "for" for_test [list_iter] list_for: "for" for_test [list_iter]
In all cases the expression formed by for_test would be subject to In all cases the expression formed by for_test would be subject to
the same precedence rules as comparisons in expressions. The two the same precedence rules as comparisons in expressions. The two
comp_operators in a for_test must be required to be both of comp_operators in a for_test must be required to be both of
similar types, unlike chained comparisons in expressions which do similar types, unlike chained comparisons in expressions which do
not have such a restriction. not have such a restriction.
We refer to the two or_expr's occurring on the left and right We refer to the two or_expr's occurring on the left and right
sides of the for-loop syntax as the bounds of the loop, and the sides of the for-loop syntax as the bounds of the loop, and the
middle or_expr as the variable of the loop. When a for-loop using middle or_expr as the variable of the loop. When a for-loop using
the new syntax is executed, the expressions for both bounds will the new syntax is executed, the expressions for both bounds will
be evaluated, and an iterator object created that iterates through be evaluated, and an iterator object created that iterates through
all integers between the two bounds according to the comparison all integers between the two bounds according to the comparison
operations used. The iterator will begin with an integer equal or operations used. The iterator will begin with an integer equal or
near to the left bound, and then step through the remaining near to the left bound, and then step through the remaining
integers with a step size of +1 or -1 if the comparison operation integers with a step size of +1 or -1 if the comparison operation
is in the set described by less_comp or greater_comp respectively. is in the set described by less_comp or greater_comp respectively.
The execution will then proceed as if the expression had been The execution will then proceed as if the expression had been::
for variable in iterator for variable in iterator
where "variable" refers to the variable of the loop and "iterator" where "variable" refers to the variable of the loop and "iterator"
refers to the iterator created for the given integer interval. refers to the iterator created for the given integer interval.
The values taken by the loop variable in an integer for-loop may The values taken by the loop variable in an integer for-loop may
be either plain integers or long integers, according to the be either plain integers or long integers, according to the
magnitude of the bounds. Both bounds of an integer for-loop must magnitude of the bounds. Both bounds of an integer for-loop must
evaluate to a real numeric type (integer, long, or float). Any evaluate to a real numeric type (integer, long, or float). Any
other value will cause the for-loop statement to raise a TypeError other value will cause the for-loop statement to raise a ``TypeError``
exception. exception.
Issues Issues
======
The following issues were raised in discussion of this and related The following issues were raised in discussion of this and related
proposals on the Python list. proposals on the Python list.
- Should the right bound be evaluated once, or every time through - Should the right bound be evaluated once, or every time through
the loop? Clearly, it only makes sense to evaluate the left the loop? Clearly, it only makes sense to evaluate the left
bound once. For reasons of consistency and efficiency, we have bound once. For reasons of consistency and efficiency, we have
chosen the same convention for the right bound. chosen the same convention for the right bound.
- Although the new syntax considerably simplifies integer - Although the new syntax considerably simplifies integer
for-loops, list comprehensions using the new syntax are not as for-loops, list comprehensions using the new syntax are not as
simple. We feel that this is appropriate since for-loops are simple. We feel that this is appropriate since for-loops are
more frequent than comprehensions. more frequent than comprehensions.
- The proposal does not allow access to integer iterator objects - The proposal does not allow access to integer iterator objects
such as would be created by xrange. True, but we see this as a such as would be created by ``xrange``. True, but we see this as a
shortcoming in the general list-comprehension syntax, beyond the shortcoming in the general list-comprehension syntax, beyond the
scope of this proposal. In addition, xrange() will still be scope of this proposal. In addition, ``xrange()`` will still be
available. available.
- The proposal does not allow increments other than 1 and -1. - The proposal does not allow increments other than 1 and -1.
More general arithmetic progressions would need to be created by More general arithmetic progressions would need to be created by
range() or xrange(), or by a list comprehension syntax such as ``range()`` or ``xrange()``, or by a list comprehension syntax such as::
[2*x for 0 <= x <= 100] [2*x for 0 <= x <= 100]
- The position of the loop variable in the middle of a three-way - The position of the loop variable in the middle of a three-way
comparison is not as apparent as the variable in the present comparison is not as apparent as the variable in the present::
for item in list for item in list
syntax, leading to a possible loss of readability. We feel that syntax, leading to a possible loss of readability. We feel that
this loss is outweighed by the increase in readability from a this loss is outweighed by the increase in readability from a
natural integer iteration syntax. natural integer iteration syntax.
- To some extent, this PEP addresses the same issues as PEP 276 - To some extent, this PEP addresses the same issues as PEP 276
[4]. We feel that the two PEPs are not in conflict since PEP [4]_. We feel that the two PEPs are not in conflict since PEP
276 is primarily concerned with half-open ranges starting in 0 276 is primarily concerned with half-open ranges starting in 0
(the easy case of range()) while this PEP is primarily concerned (the easy case of ``range()``) while this PEP is primarily concerned
with simplifying all other cases. However, if this PEP is with simplifying all other cases. However, if this PEP is
approved, its new simpler syntax for integer loops could to some approved, its new simpler syntax for integer loops could to some
extent reduce the motivation for PEP 276. extent reduce the motivation for PEP 276.
- It is not clear whether it makes sense to allow floating point - It is not clear whether it makes sense to allow floating point
bounds for an integer loop: if a float represents an inexact bounds for an integer loop: if a float represents an inexact
value, how can it be used to determine an exact sequence of value, how can it be used to determine an exact sequence of
integers? On the other hand, disallowing float bounds would integers? On the other hand, disallowing float bounds would
make it difficult to use floor() and ceiling() in integer make it difficult to use ``floor()`` and ``ceiling()`` in integer
for-loops, as it is difficult to use them now with range(). We for-loops, as it is difficult to use them now with ``range()``. We
have erred on the side of flexibility, but this may lead to some have erred on the side of flexibility, but this may lead to some
implementation difficulties in determining the smallest and implementation difficulties in determining the smallest and
largest integer values that would cause a given comparison to be largest integer values that would cause a given comparison to be
true. true.
- Should types other than int, long, and float be allowed as - Should types other than int, long, and float be allowed as
bounds? Another choice would be to convert all bounds to bounds? Another choice would be to convert all bounds to
integers by int(), and allow as bounds anything that can be so integers by ``int()``, and allow as bounds anything that can be so
converted instead of just floats. However, this would change converted instead of just floats. However, this would change
the semantics: 0.3 <= x is not the same as int(0.3) <= x, and it the semantics: ``0.3 <= x`` is not the same as ``int(0.3) <= x``, and it
would be confusing for a loop with 0.3 as lower bound to start would be confusing for a loop with 0.3 as lower bound to start
at zero. Also, in general int(f) can be very far from f. at zero. Also, in general ``int(f)`` can be very far from ``f``.
Implementation Implementation
==============
An implementation is not available at this time. Implementation An implementation is not available at this time. Implementation
is not expected to pose any great difficulties: the new syntax is not expected to pose any great difficulties: the new syntax
could, if necessary, be recognized by parsing a general expression could, if necessary, be recognized by parsing a general expression
after each "for" keyword and testing whether the top level after each "for" keyword and testing whether the top level
operation of the expression is "in" or a three-way comparison. operation of the expression is "in" or a three-way comparison.
The Python compiler would convert any instance of the new syntax The Python compiler would convert any instance of the new syntax
into a loop over the items in a special iterator object. into a loop over the items in a special iterator object.
References References
==========
[1] PEP 204, Range Literals .. [1] PEP 204, Range Literals
http://www.python.org/dev/peps/pep-0204/ http://www.python.org/dev/peps/pep-0204/
[2] PEP 212, Loop Counter Iteration .. [2] PEP 212, Loop Counter Iteration
http://www.python.org/dev/peps/pep-0212/ http://www.python.org/dev/peps/pep-0212/
[3] PEP 281, Loop Counter Iteration with range and xrange .. [3] PEP 281, Loop Counter Iteration with range and xrange
http://www.python.org/dev/peps/pep-0281/ http://www.python.org/dev/peps/pep-0281/
[4] PEP 276, Simple Iterator for ints .. [4] PEP 276, Simple Iterator for ints
http://www.python.org/dev/peps/pep-0276/ http://www.python.org/dev/peps/pep-0276/
.. [5] Raymond Hettinger, Propose updating PEP 284 -- Integer for-loops
https://mail.python.org/pipermail/python-dev/2005-June/054316.html
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
fill-column: 70 indent-tabs-mode: nil
End: fill-column: 70
End:

View File

@ -3,241 +3,259 @@ Title: Reliable Acquisition/Release Pairs
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Michael Hudson <mwh@python.net>, Author: Michael Hudson <mwh@python.net>,
Paul Moore <p.f.moore@gmail.com> Paul Moore <p.f.moore@gmail.com>
Status: Rejected Status: Rejected
Type: Standards Track Type: Standards Track
Content-Type: text/plain Content-Type: text/x-rst
Created: 18-Dec-2002 Created: 18-Dec-2002
Python-Version: 2.4 Python-Version: 2.4
Post-History: Post-History:
Abstract Abstract
========
It would be nice to have a less typing-intense way of writing: It would be nice to have a less typing-intense way of writing::
the_lock.acquire() the_lock.acquire()
try: try:
.... ....
finally: finally:
the_lock.release() the_lock.release()
This PEP proposes a piece of syntax (a 'with' block) and a
"small-i" interface that generalizes the above.
This PEP proposes a piece of syntax (a 'with' block) and a
"small-i" interface that generalizes the above.
Pronouncement Pronouncement
=============
This PEP is rejected in favor of PEP 343.
This PEP is rejected in favor of PEP 343.
Rationale Rationale
=========
One of the advantages of Python's exception handling philosophy is 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 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 check the return value of some system call). Currently, this does
not apply to resource cleanup. The current syntax for acquisition not apply to resource cleanup. The current syntax for acquisition
and release of a resource (for example, a lock) is and release of a resource (for example, a lock) is::
the_lock.acquire() the_lock.acquire()
try: try:
.... ....
finally: finally:
the_lock.release() the_lock.release()
This syntax separates the acquisition and release by a (possibly This syntax separates the acquisition and release by a (possibly
large) block of code, which makes it difficult to confirm "at a large) block of code, which makes it difficult to confirm "at a
glance" that the code manages the resource correctly. Another glance" that the code manages the resource correctly. Another
common error is to code the "acquire" call within the try block, common error is to code the "acquire" call within the try block,
which incorrectly releases the lock if the acquire fails. which incorrectly releases the lock if the acquire fails.
Basic Syntax and Semantics Basic Syntax and Semantics
==========================
The syntax of a 'with' statement is as follows:: The syntax of a 'with' statement is as follows::
'with' [ var '=' ] expr ':' 'with' [ var '=' ] expr ':'
suite suite
This statement is defined as being equivalent to the following This statement is defined as being equivalent to the following
sequence of statements: sequence of statements::
var = expr var = expr
if hasattr(var, "__enter__"): if hasattr(var, "__enter__"):
var.__enter__() var.__enter__()
try: try:
suite suite
finally: finally:
var.__exit__() var.__exit__()
(The presence of an __exit__ method is *not* checked like that of (The presence of an ``__exit__`` method is *not* checked like that of
__enter__ to ensure that using inappropriate objects in with: ``__enter__`` to ensure that using inappropriate objects in with:
statements gives an error). statements gives an error).
If the variable is omitted, an unnamed object is allocated on the 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. stack. In that case, the suite has no access to the unnamed object.
Possible Extensions Possible Extensions
===================
A number of potential extensions to the basic syntax have been A number of potential extensions to the basic syntax have been
discussed on the Python Developers list. None of these extensions discussed on the Python Developers list. None of these extensions
are included in the solution proposed by this PEP. In many cases, are included in the solution proposed by this PEP. In many cases,
the arguments are nearly equally strong in both directions. In the arguments are nearly equally strong in both directions. In
such cases, the PEP has always chosen simplicity, simply because such cases, the PEP has always chosen simplicity, simply because
where extra power is needed, the existing try block is available. where extra power is needed, the existing try block is available.
Multiple expressions Multiple expressions
--------------------
One proposal was for allowing multiple expressions within one One proposal was for allowing multiple expressions within one
'with' statement. The __enter__ methods would be called left to 'with' statement. The ``__enter__`` methods would be called left to
right, and the __exit__ methods right to left. The advantage of right, and the ``__exit__`` methods right to left. The advantage of
doing so is that where more than one resource is being managed, doing so is that where more than one resource is being managed,
nested 'with' statements will result in code drifting towards the nested 'with' statements will result in code drifting towards the
right margin. The solution to this problem is the same as for any 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 other deep nesting - factor out some of the code into a separate
function. Furthermore, the question of what happens if one of the function. Furthermore, the question of what happens if one of the
__exit__ methods raises an exception (should the other __exit__ ``__exit__`` methods raises an exception (should the other ``__exit__``
methods be called?) needs to be addressed. methods be called?) needs to be addressed.
Exception handling Exception handling
------------------
An extension to the protocol to include an optional __except__ An extension to the protocol to include an optional ``__except__``
handler, which is called when an exception is raised, and which handler, which is called when an exception is raised, and which
can handle or re-raise the exception, has been suggested. It is 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 not at all clear that the semantics of this extension can be made
precise and understandable. For example, should the equivalent precise and understandable. For example, should the equivalent
code be try ... except ... else if an exception handler is code be ``try ... except ... else`` if an exception handler is
defined, and try ... finally if not? How can this be determined defined, and ``try ... finally`` if not? How can this be determined
at compile time, in general? The alternative is to define the at compile time, in general? The alternative is to define the
code as expanding to a try ... except inside a try ... finally. code as expanding to a ``try ... except`` inside a ``try ... finally``.
But this may not do the right thing in real life. But this may not do the right thing in real life.
The only use case identified for exception handling is with The only use case identified for exception handling is with
transactional processing (commit on a clean finish, and rollback transactional processing (commit on a clean finish, and rollback
on an exception). This is probably just as easy to handle with a on an exception). This is probably just as easy to handle with a
conventional try ... except ... else block, and so the PEP does conventional ``try ... except ... else`` block, and so the PEP does
not include any support for exception handlers. not include any support for exception handlers.
Implementation Notes Implementation Notes
====================
There is a potential race condition in the code specified as There is a potential race condition in the code specified as
equivalent to the with statement. For example, if a equivalent to the with statement. For example, if a
KeyboardInterrupt exception is raised between the completion of ``KeyboardInterrupt`` exception is raised between the completion of
the __enter__ method call and the start of the try block, the the ``__enter__`` method call and the start of the try block, the
__exit__ method will not be called. This can lead to resource ``__exit__`` method will not be called. This can lead to resource
leaks, or to deadlocks. [XXX Guido has stated that he cares about 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 this sort of race condition, and intends to write some C magic to
handle them. The implementation of the 'with' statement should handle them. The implementation of the 'with' statement should
copy this.] copy this.]
Open Issues Open Issues
===========
Should existing classes (for example, file-like objects and locks) Should existing classes (for example, file-like objects and locks)
gain appropriate __enter__ and __exit__ methods? The obvious gain appropriate ``__enter__`` and ``__exit__`` methods? The obvious
reason in favour is convenience (no adapter needed). The argument reason in favour is convenience (no adapter needed). The argument
against is that if built-in files have this but (say) StringIO 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 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 reused with a ``StringIO`` object. So ``__exit__ = close`` becomes a part
of the "file-like object" protocol, which user-defined classes may of the "file-like object" protocol, which user-defined classes may
need to support. need to support.
The __enter__ hook may be unnecessary - for many use cases, an The ``__enter__`` hook may be unnecessary - for many use cases, an
adapter class is needed and in that case, the work done by the 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. ``__enter__`` hook can just as easily be done in the ``__init__`` hook.
If a way of controlling object lifetimes explicitly was available, If a way of controlling object lifetimes explicitly was available,
the function of the __exit__ hook could be taken over by the the function of the ``__exit__`` hook could be taken over by the
existing __del__ hook. An email exchange[1] with a proponent of existing ``__del__`` hook. An email exchange [1]_ with a proponent of
this approach left one of the authors even more convinced that this approach left one of the authors even more convinced that
it isn't the right idea... it isn't the right idea...
It has been suggested[2] that the "__exit__" method be called It has been suggested [2]_ that the "__exit__" method be called
"close", or that a "close" method should be considered if no "close", or that a "close" method should be considered if no
__exit__ method is found, to increase the "out-of-the-box utility" ``__exit__`` method is found, to increase the "out-of-the-box utility"
of the "with ..." construct. of the "with ..." construct.
There are some similarities in concept between 'with ...' blocks There are some similarities in concept between 'with ...' blocks
and generators, which have led to proposals that for loops could and generators, which have led to proposals that for loops could
implement the with block functionality[3]. While neat on some implement the with block functionality [3]_. While neat on some
levels, we think that for loops should stick to being loops. levels, we think that for loops should stick to being loops.
Alternative Ideas Alternative Ideas
=================
IEXEC: Holger Krekel -- generalised approach with XML-like syntax IEXEC: Holger Krekel -- generalised approach with XML-like syntax
(no URL found...) (no URL found...)
Holger has much more far-reaching ideas about "execution monitors" Holger has much more far-reaching ideas about "execution monitors"
that are informed about details of control flow in the monitored that are informed about details of control flow in the monitored
block. While interesting, these ideas could change the language block. While interesting, these ideas could change the language
in deep and subtle ways and as such belong to a different PEP. in deep and subtle ways and as such belong to a different PEP.
Any Smalltalk/Ruby anonymous block style extension obviously Any Smalltalk/Ruby anonymous block style extension obviously
subsumes this one. subsumes this one.
PEP 319 is in the same area, but did not win support when aired on PEP 319 is in the same area, but did not win support when aired on
python-dev. python-dev.
Backwards Compatibility Backwards Compatibility
=======================
This PEP proposes a new keyword, so the __future__ game will need This PEP proposes a new keyword, so the ``__future__`` game will need
to be played. to be played.
Cost of Adoption Cost of Adoption
================
Those who claim the language is getting larger and more Those who claim the language is getting larger and more
complicated have something else to complain about. It's something complicated have something else to complain about. It's something
else to teach. else to teach.
For the proposal to be useful, many file-like and lock-like For the proposal to be useful, many file-like and lock-like
classes in the standard library and other code will have to have classes in the standard library and other code will have to have::
__exit__ = close __exit__ = close
or similar added. or similar added.
Cost of Non-Adoption Cost of Non-Adoption
====================
Writing correct code continues to be more effort than writing Writing correct code continues to be more effort than writing
incorrect code. incorrect code.
References References
==========
There are various python-list and python-dev discussions that There are various python-list and python-dev discussions that
could be mentioned here. could be mentioned here.
[1] Off-list conversation between Michael Hudson and Bill Soudan .. [1] Off-list conversation between Michael Hudson and Bill Soudan
(made public with permission) (made public with permission)
http://starship.python.net/crew/mwh/pep310/ http://starship.python.net/crew/mwh/pep310/
[2] Samuele Pedroni on python-dev .. [2] Samuele Pedroni on python-dev
http://mail.python.org/pipermail/python-dev/2003-August/037795.html http://mail.python.org/pipermail/python-dev/2003-August/037795.html
[3] Thread on python-dev with subject .. [3] Thread on python-dev with subject
[Python-Dev] pre-PEP: Resource-Release Support for Generators .. [Python-Dev] pre-PEP: Resource-Release Support for Generators
starting at starting at
http://mail.python.org/pipermail/python-dev/2003-August/037803.html http://mail.python.org/pipermail/python-dev/2003-August/037803.html
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
sentence-end-double-space: t indent-tabs-mode: nil
fill-column: 70 sentence-end-double-space: t
End: fill-column: 70
End: