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>
Status: Deferred
Type: Standards Track
Content-Type: text/x-rst
Created: 18-Aug-2000
Python-Version: 2.1
Post-History: 22-Dec-2000
Abstract
========
This PEP proposes a set of enhancements to the CGI development
facilities in the Python standard library. Enhancements might be
new features, new modules for tasks such as cookie support, or
removal of obsolete code.
This PEP proposes a set of enhancements to the CGI development
facilities in the Python standard library. Enhancements might be
new features, new modules for tasks such as cookie support, or
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
===========
This section lists changes that have been suggested, but about
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
be classified as accepted or rejected.
This section lists changes that have been suggested, but about
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
be classified as accepted or rejected.
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
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
reads it into a second temp file, then into a final destination!
And even if we did, that would mean creating yet another object
with its __init__ call and associated overhead.
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
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
reads it into a second temp file, then into a final destination!
And even if we did, that would mean creating yet another object
with its ``__init__`` call and associated overhead.
cgi.py: Currently, query data with no `=' are ignored. Even if
keep_blank_values is set, queries like `...?value=&...' are
returned with blank values but queries like `...?value&...' are
completely lost. It would be great if such data were made
available through the FieldStorage interface, either as entries
with None as values, or in a separate list.
cgi.py: Currently, query data with no ``=`` are ignored. Even if
keep_blank_values is set, queries like ``...?value=&...`` are
returned with blank values but queries like ``...?value&...`` are
completely lost. It would be great if such data were made
available through the ``FieldStorage`` interface, either as entries
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
===========
This section lists details about entire new packages or modules
that should be added to the Python standard library.
This section lists details about entire new packages or modules
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
=================================
This section lists details of major changes to existing modules,
whether in implementation or in interface. The changes in this
section therefore carry greater degrees of risk, either in
introducing bugs or a backward incompatibility.
This section lists details of major changes to existing modules,
whether in implementation or in interface. The changes in this
section therefore carry greater degrees of risk, either in
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
=================================
This section lists details of minor changes to existing modules.
These changes should have relatively small implementations, and
have little risk of introducing incompatibilities with previous
versions.
This section lists details of minor changes to existing modules.
These changes should have relatively small implementations, and
have little risk of introducing incompatibilities with previous
versions.
Rejected Changes
================
The changes listed in this section were proposed for Python 2.1,
but were rejected as unsuitable. For each rejected change, a
rationale is given describing why the change was deemed
inappropriate.
The changes listed in this section were proposed for Python 2.1,
but were rejected as unsuitable. For each rejected change, a
rationale is given describing why the change was deemed
inappropriate.
* An HTML generation module is not part of this PEP. Several such
modules exist, ranging from HTMLgen's purely programming
interface to ASP-inspired simple templating to DTML's complex
templating. There's no indication of which templating module to
enshrine in the standard library, and that probably means that
no module should be so chosen.
* An HTML generation module is not part of this PEP. Several such
modules exist, ranging from HTMLgen's purely programming
interface to ASP-inspired simple templating to DTML's complex
templating. There's no indication of which templating module to
enshrine in the standard library, and that probably means that
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
==================
XXX open issues: naming convention (studlycaps or
underline-separated?); need to look at the cgi.parse*() functions
and see if they can be simplified, too.
XXX open issues: naming convention (studlycaps or
underline-separated?); need to look at the ``cgi.parse*()`` functions
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
# HTTPResponse class.
class Response:
"""
Attributes:
@ -116,57 +129,57 @@ Proposed Interface
headers: dictionary of response headers
body: string containing the body of the HTTP response
"""
def __init__(self, status=200, headers={}, body=""):
pass
def setStatus(self, status, reason=None):
"Set the numeric HTTP response code"
pass
def setHeader(self, name, value):
"Set an HTTP header"
pass
def setBody(self, body):
"Set the body of the response"
pass
def setCookie(self, name, value,
path = '/',
comment = None,
domain = None,
path = '/',
comment = None,
domain = None,
max-age = None,
expires = None,
secure = 0
):
"Set a cookie"
pass
def expireCookie(self, name):
"Remove a cookie from the user"
pass
def redirect(self, url):
"Redirect the browser to another URL"
pass
def __str__(self):
"Convert entire response to a string"
pass
def dump(self):
"Return a string representation useful for debugging"
pass
# XXX methods for specific classes of error:serverError,
# XXX methods for specific classes of error:serverError,
# badRequest, etc.?
class Request:
"""
Attributes:
Attributes:
XXX should these be dictionaries, or dictionary-like objects?
.headers : dictionary containing HTTP headers
@ -174,31 +187,31 @@ Proposed Interface
.fields : data from the form
.env : environment dictionary
"""
def __init__(self, environ=os.environ, stdin=sys.stdin,
keep_blank_values=1, strict_parsing=0):
"""Initialize the request object, using the provided environment
and standard input."""
pass
# Should people just use the dictionaries directly?
def getHeader(self, name, default=None):
pass
def getCookie(self, name, default=None):
pass
def getField(self, name, default=None):
"Return field's value as a string (even if it's an uploaded file)"
pass
def getUploadedFile(self, name):
"""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
a StringIO around simple fields for consistency?
"""
def getURL(self, n=0, query_string=0):
"""Return the URL of the current request, chopping off 'n' path
components from the right. Eg. if the URL is
@ -209,48 +222,50 @@ Proposed Interface
def getBaseURL(self, n=0):
"""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
"http://foo.com/q/bar/baz/qux", n=0 would return
"http://foo.com/", and n=2 "http://foo.com/q/bar".
Returned URL does not include the query string, if any.
"""
def dump(self):
"String representation suitable for debugging output"
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.
def getBrowser(self):
"Returns Mozilla/IE/Lynx/Opera/whatever"
def isSecure(self):
"Return true if this is an SSLified request"
# Module-level function
# Module-level function
def wrapper(func, logfile=sys.stderr):
"""
Calls the function 'func', passing it the arguments
(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,
# 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.
# (XXX how to do file uploads in this syntax?)
Copyright
This document has been placed in the public domain.
=========
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

@ -5,253 +5,272 @@ Last-Modified: $Date$
Author: mal@lemburg.com (Marc-André Lemburg)
Status: Rejected
Type: Standards Track
Content-Type: text/x-rst
Created: 23-Aug-2000
Python-Version: 2.1
Post-History:
Introduction
============
This PEP describes the "attribute docstring" proposal for Python
2.0. This PEP tracks the status and ownership of this feature.
It contains a description of the feature and outlines changes
necessary to support the feature. The CVS revision history of
this file contains the definitive historical record.
This PEP describes the "attribute docstring" proposal for Python
2.0. This PEP tracks the status and ownership of this feature.
It contains a description of the feature and outlines changes
necessary to support the feature. The CVS revision history of
this file contains the definitive historical record.
Rationale
=========
This PEP proposes a small addition to the way Python currently
handles docstrings embedded in Python code.
This PEP proposes a small addition to the way Python currently
handles docstrings embedded in Python code.
Python currently only handles the case of docstrings which appear
directly after a class definition, a function definition or as
first string literal in a module. The string literals are added
to the objects in question under the __doc__ attribute and are
from then on available for introspection tools which can extract
the contained information for help, debugging and documentation
purposes.
Python currently only handles the case of docstrings which appear
directly after a class definition, a function definition or as
first string literal in a module. The string literals are added
to the objects in question under the ``__doc__`` attribute and are
from then on available for introspection tools which can extract
the contained information for help, debugging and documentation
purposes.
Docstrings appearing in locations other than the ones mentioned
are simply ignored and don't result in any code generation.
Docstrings appearing in locations other than the ones mentioned
are simply ignored and don't result in any code generation.
Here is an example:
Here is an example::
class C:
"class C doc-string"
class C:
"class C doc-string"
a = 1
"attribute C.a doc-string (1)"
a = 1
"attribute C.a doc-string (1)"
b = 2
"attribute C.b doc-string (2)"
b = 2
"attribute C.b doc-string (2)"
The docstrings (1) and (2) are currently being ignored by the
Python byte code compiler, but could obviously be put to good use
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 docstrings (1) and (2) are currently being ignored by the
Python byte code compiler, but could obviously be put to good use
for documenting the named assignments that precede them.
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.
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
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
==============
Docstrings are handled by the byte code compiler as expressions.
The current implementation special cases the few locations
mentioned above to make use of these expressions, but otherwise
ignores the strings completely.
Docstrings are handled by the byte code compiler as expressions.
The current implementation special cases the few locations
mentioned above to make use of these expressions, but otherwise
ignores the strings completely.
To enable use of these docstrings for documenting named
assignments (which is the natural way of defining e.g. class
attributes), the compiler will have to keep track of the last
assigned name and then use this name to assign the content of the
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
namespace during object construction time.
To enable use of these docstrings for documenting named
assignments (which is the natural way of defining e.g. class
attributes), the compiler will have to keep track of the last
assigned name and then use this name to assign the content of the
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
namespace during object construction time.
In order to preserve features like inheritance and hiding of
Python's special attributes (ones with leading and trailing double
underscores), a special name mangling has to be applied which
uniquely identifies the docstring as belonging to the name
assignment and allows finding the docstring later on by inspecting
the namespace.
In order to preserve features like inheritance and hiding of
Python's special attributes (ones with leading and trailing double
underscores), a special name mangling has to be applied which
uniquely identifies the docstring as belonging to the name
assignment and allows finding the docstring later on by inspecting
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
stores this name in a variable of the compiling structure. This
variable defaults to NULL. When it sees a docstring, it then
checks the variable and uses the name as basis for the above name
mangling to produce an implicit assignment of the docstring to the
mangled name. It then resets the variable to NULL to avoid
duplicate assignments.
To keep track of the last assigned name, the byte code compiler
stores this name in a variable of the compiling structure. This
variable defaults to NULL. When it sees a docstring, it then
checks the variable and uses the name as basis for the above name
mangling to produce an implicit assignment of the docstring to the
mangled name. It then resets the variable to NULL to avoid
duplicate assignments.
If the variable does not point to a name (i.e. is NULL), no
assignments are made. These will continue to be ignored like
before. All classical docstrings fall under this case, so no
duplicate assignments are done.
If the variable does not point to a name (i.e. is NULL), no
assignments are made. These will continue to be ignored like
before. All classical docstrings fall under this case, so no
duplicate assignments are done.
In the above example this would result in the following new class
attributes to be created:
In the above example this would result in the following new class
attributes to be created::
C.__doc_a__ == "attribute C.a doc-string (1)"
C.__doc_b__ == "attribute C.b doc-string (2)"
C.__doc_a__ == "attribute C.a doc-string (1)"
C.__doc_b__ == "attribute C.b doc-string (2)"
A patch to the current CVS version of Python 2.0 which implements
the above is available on SourceForge at [1].
A patch to the current CVS version of Python 2.0 which implements
the above is available on SourceForge at [1]_.
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
separated by other expressions:
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.
class C:
"C doc string"
This can lead to cases where the docstring and assignment may be
separated by other expressions::
b = 2
class C:
"C doc string"
def x(self):
"C.x doc string"
y = 3
return 1
b = 2
"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
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__.
"b's doc string"
A possible solution to this problem would be resetting the name
variable for all non-expression nodes in the compiler.
Since the definition of method "x" currently does not reset the
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
=================
Even though highly unlikely, attribute docstrings could get
accidentally concatenated to the attribute's value:
Even though highly unlikely, attribute docstrings could get
accidentally concatenated to the attribute's value::
class C:
x = "text" \
"x's docstring"
class C:
x = "text" \
"x's docstring"
The trailing slash would cause the Python compiler to concatenate
the attribute value and the docstring.
The trailing slash would cause the Python compiler to concatenate
the attribute value and the docstring.
A modern syntax highlighting editor would easily make this
accident visible, though, and by simply inserting emtpy lines
between the attribute definition and the docstring you can avoid
the possible concatenation completely, so the problem is
negligible.
A modern syntax highlighting editor would easily make this
accident visible, though, and by simply inserting emtpy lines
between the attribute definition and the docstring you can avoid
the possible concatenation completely, so the problem is
negligible.
Another possible problem is that of using triple quoted strings as
a way to uncomment parts of your code.
Another possible problem is that of using triple quoted strings as
a way to uncomment parts of your code.
If there happens to be an assignment just before the start of the
comment string, then the compiler will treat the comment as
docstring attribute and apply the above logic to it.
If there happens to be an assignment just before the start of the
comment string, then the compiler will treat the comment as
docstring attribute and apply the above logic to it.
Besides generating a docstring for an otherwise undocumented
attribute there is no breakage.
Besides generating a docstring for an otherwise undocumented
attribute there is no breakage.
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
it's not of great importance to me) but there are two things I
don't like in your current proposal:
I "kinda" like the idea of having attribute docstrings (meaning
it's not of great importance to me) but there are two things I
don't like in your current proposal:
1. The syntax you propose is too ambiguous: as you say,
stand-alone string literal are used for other purposes and could
suddenly become attribute docstrings.
1. The syntax you propose is too ambiguous: as you say,
stand-alone string literal are used for other purposes and could
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
> string literal are used for other purposes and could suddenly
> 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__)
* 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)
> 2. I don't like the access method either (``__doc_<attrname>__``).
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:
Any other name will do. It will only have to match these
criteria:
...
* 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
for a or for b.
It might be useful, but I really hate the proposed syntax.
...
You can use this convention:
::
a = 1
__doc_a__ = "doc string for a"
a = 1
"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
> other users on c.l.p and in private emails have supported it
> at the time I wrote the PEP.
...
You can use this convention::
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
=========
This document has been placed in the Public Domain.
This document has been placed in the Public Domain.
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
indent-tabs-mode: nil
End:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:

View File

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

View File

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

View File

@ -3,241 +3,259 @@ Title: Reliable Acquisition/Release Pairs
Version: $Revision$
Last-Modified: $Date$
Author: Michael Hudson <mwh@python.net>,
Paul Moore <p.f.moore@gmail.com>
Paul Moore <p.f.moore@gmail.com>
Status: Rejected
Type: Standards Track
Content-Type: text/plain
Content-Type: text/x-rst
Created: 18-Dec-2002
Python-Version: 2.4
Post-History:
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()
try:
....
finally:
the_lock.release()
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.
This PEP proposes a piece of syntax (a 'with' block) and a
"small-i" interface that generalizes the above.
Pronouncement
=============
This PEP is rejected in favor of PEP 343.
This PEP is rejected in favor of PEP 343.
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
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()
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.
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::
The syntax of a 'with' statement is as follows::
'with' [ var '=' ] expr ':'
suite
suite
This statement is defined as being equivalent to the following
sequence of statements:
This statement is defined as being equivalent to the following
sequence of statements::
var = expr
if hasattr(var, "__enter__"):
var.__enter__()
var.__enter__()
try:
suite
suite
finally:
var.__exit__()
var.__exit__()
(The presence of an __exit__ method is *not* checked like that of
__enter__ to ensure that using inappropriate objects in with:
statements gives an error).
(The presence of an ``__exit__`` method is *not* checked like that of
``__enter__`` to ensure that using inappropriate objects in with:
statements gives an error).
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.
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.
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
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.
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
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.
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.
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
====================
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.]
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.
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.
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. An email exchange[1] with a proponent of
this approach left one of the authors even more convinced that
it isn't the right idea...
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. An email exchange [1]_ with a proponent of
this approach left one of the authors even more convinced that
it isn't the right idea...
It has been suggested[2] that the "__exit__" method be called
"close", or that a "close" method should be considered if no
__exit__ method is found, to increase the "out-of-the-box utility"
of the "with ..." construct.
It has been suggested [2]_ that the "__exit__" method be called
"close", or that a "close" method should be considered if no
``__exit__`` method is found, to increase the "out-of-the-box utility"
of the "with ..." construct.
There are some similarities in concept between 'with ...' blocks
and generators, which have led to proposals that for loops could
implement the with block functionality[3]. While neat on some
levels, we think that for loops should stick to being loops.
There are some similarities in concept between 'with ...' blocks
and generators, which have led to proposals that for loops could
implement the with block functionality [3]_. While neat on some
levels, we think that for loops should stick to being loops.
Alternative Ideas
=================
IEXEC: Holger Krekel -- generalised approach with XML-like syntax
(no URL found...)
IEXEC: Holger Krekel -- generalised approach with XML-like syntax
(no URL found...)
Holger has much more far-reaching ideas about "execution monitors"
that are informed about details of control flow in the monitored
block. While interesting, these ideas could change the language
in deep and subtle ways and as such belong to a different PEP.
Holger has much more far-reaching ideas about "execution monitors"
that are informed about details of control flow in the monitored
block. While interesting, these ideas could change the language
in deep and subtle ways and as such belong to a different PEP.
Any Smalltalk/Ruby anonymous block style extension obviously
subsumes this one.
Any Smalltalk/Ruby anonymous block style extension obviously
subsumes this one.
PEP 319 is in the same area, but did not win support when aired on
python-dev.
PEP 319 is in the same area, but did not win support when aired on
python-dev.
Backwards Compatibility
=======================
This PEP proposes a new keyword, so the __future__ game will need
to be played.
This PEP proposes a new keyword, so the ``__future__`` game will need
to be played.
Cost of Adoption
================
Those who claim the language is getting larger and more
complicated have something else to complain about. It's something
else to teach.
Those who claim the language is getting larger and more
complicated have something else to complain about. It's something
else to teach.
For the proposal to be useful, many file-like and lock-like
classes in the standard library and other code will have to have
For the proposal to be useful, many file-like and lock-like
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
====================
Writing correct code continues to be more effort than writing
incorrect code.
Writing correct code continues to be more effort than writing
incorrect code.
References
==========
There are various python-list and python-dev discussions that
could be mentioned here.
There are various python-list and python-dev discussions that
could be mentioned here.
[1] Off-list conversation between Michael Hudson and Bill Soudan
(made public with permission)
http://starship.python.net/crew/mwh/pep310/
.. [1] Off-list conversation between Michael Hudson and Bill Soudan
(made public with permission)
http://starship.python.net/crew/mwh/pep310/
[2] Samuele Pedroni on python-dev
http://mail.python.org/pipermail/python-dev/2003-August/037795.html
.. [2] Samuele Pedroni on python-dev
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
=========
This document has been placed in the public domain.
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:
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End: