Clarify the original meanings I intended for various terms, and record the fact that gaining consensus on the terminology is still an open issue
This commit is contained in:
parent
f554d12d6f
commit
db32145d0e
165
pep-0343.txt
165
pep-0343.txt
|
@ -7,7 +7,7 @@ Status: Accepted
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
Content-Type: text/plain
|
Content-Type: text/plain
|
||||||
Created: 13-May-2005
|
Created: 13-May-2005
|
||||||
Post-History: 2-Jun-2005, 16-Oct-2005, 29-Oct-2005
|
Post-History: 2-Jun-2005, 16-Oct-2005, 29-Oct-2005, 23-Apr-2006
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ Abstract
|
||||||
section on Resolved Issues). It's still at Draft status until
|
section on Resolved Issues). It's still at Draft status until
|
||||||
Guido gives a final blessing to the updated PEP.
|
Guido gives a final blessing to the updated PEP.
|
||||||
|
|
||||||
|
|
||||||
Author's Note
|
Author's Note
|
||||||
|
|
||||||
This PEP was originally written in first person by Guido, and
|
This PEP was originally written in first person by Guido, and
|
||||||
|
@ -26,6 +27,13 @@ Author's Note
|
||||||
on python-dev. Any first person references are from Guido's
|
on python-dev. Any first person references are from Guido's
|
||||||
original.
|
original.
|
||||||
|
|
||||||
|
Python's alpha release cycle revealed terminology problems in this
|
||||||
|
PEP and in the associated documentation and implementation [14].
|
||||||
|
The current version of the PEP reflects the implementation and
|
||||||
|
documentation as at Python 2.5a2. The PEP will be updated to
|
||||||
|
reflect any changes made to the terminology prior to the final
|
||||||
|
Python 2.5 release.
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
|
||||||
After a lot of discussion about PEP 340 and alternatives, I
|
After a lot of discussion about PEP 340 and alternatives, I
|
||||||
|
@ -225,9 +233,9 @@ Specification: The 'with' Statement
|
||||||
|
|
||||||
The translation of the above statement is:
|
The translation of the above statement is:
|
||||||
|
|
||||||
ctx = (EXPR).__context__()
|
mgr = (EXPR).__context__()
|
||||||
exit = ctx.__exit__ # Not calling it yet
|
exit = mgr.__exit__ # Not calling it yet
|
||||||
value = ctx.__enter__()
|
value = mgr.__enter__()
|
||||||
exc = True
|
exc = True
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -244,7 +252,7 @@ Specification: The 'with' Statement
|
||||||
if exc:
|
if exc:
|
||||||
exit(None, None, None)
|
exit(None, None, None)
|
||||||
|
|
||||||
Here, the lowercase variables (ctx, exit, value, exc) are internal
|
Here, the lowercase variables (mgr, exit, value, exc) are internal
|
||||||
variables and not accessible to the user; they will most likely be
|
variables and not accessible to the user; they will most likely be
|
||||||
implemented as special registers or stack positions.
|
implemented as special registers or stack positions.
|
||||||
|
|
||||||
|
@ -260,37 +268,37 @@ Specification: The 'with' Statement
|
||||||
as exceptions by __exit__().)
|
as exceptions by __exit__().)
|
||||||
|
|
||||||
The call to the __context__() method serves a similar purpose to
|
The call to the __context__() method serves a similar purpose to
|
||||||
that of the __iter__() method of iterator and iterables. An
|
that of the __iter__() method of iterator and iterables. A context
|
||||||
object with with simple state requirements (such as
|
object with simple state requirements (such as
|
||||||
threading.RLock) may provide its own __enter__() and __exit__()
|
threading.RLock) may provide its own __enter__() and __exit__()
|
||||||
methods, and simply return 'self' from its __context__ method. On
|
methods, and simply return 'self' from its __context__ method. On
|
||||||
the other hand, an object with more complex state requirements
|
the other hand, a context object with more complex state requirements
|
||||||
(such as decimal.Context) may return a distinct context object
|
(such as decimal.Context) may return a distinct context manager
|
||||||
each time its __context__ method is invoked.
|
each time its __context__ method is invoked.
|
||||||
|
|
||||||
If the "as VAR" part of the syntax is omitted, the "VAR =" part of
|
If the "as VAR" part of the syntax is omitted, the "VAR =" part of
|
||||||
the translation is omitted (but ctx.__enter__() is still called).
|
the translation is omitted (but mgr.__enter__() is still called).
|
||||||
|
|
||||||
The calling convention for ctx.__exit__() is as follows. If the
|
The calling convention for mgr.__exit__() is as follows. If the
|
||||||
finally-suite was reached through normal completion of BLOCK or
|
finally-suite was reached through normal completion of BLOCK or
|
||||||
through a non-local goto (a break, continue or return statement in
|
through a non-local goto (a break, continue or return statement in
|
||||||
BLOCK), ctx.__exit__() is called with three None arguments. If
|
BLOCK), mgr.__exit__() is called with three None arguments. If
|
||||||
the finally-suite was reached through an exception raised in
|
the finally-suite was reached through an exception raised in
|
||||||
BLOCK, ctx.__exit__() is called with three arguments representing
|
BLOCK, mgr.__exit__() is called with three arguments representing
|
||||||
the exception type, value, and traceback.
|
the exception type, value, and traceback.
|
||||||
|
|
||||||
IMPORTANT: if ctx.__exit__() returns a "true" value, the exception
|
IMPORTANT: if mgr.__exit__() returns a "true" value, the exception
|
||||||
is "swallowed". That is, if it returns "true", execution
|
is "swallowed". That is, if it returns "true", execution
|
||||||
continues at the next statement after the with-statement, even if
|
continues at the next statement after the with-statement, even if
|
||||||
an exception happened inside the with-statement. However, if the
|
an exception happened inside the with-statement. However, if the
|
||||||
with-statement was left via a non-local goto (break, continue or
|
with-statement was left via a non-local goto (break, continue or
|
||||||
return), this non-local return is resumed when ctx.__exit__()
|
return), this non-local return is resumed when mgr.__exit__()
|
||||||
returns regardless of the return value. The motivation for this
|
returns regardless of the return value. The motivation for this
|
||||||
detail is to make it possible for ctx.__exit__() to swallow
|
detail is to make it possible for mgr.__exit__() to swallow
|
||||||
exceptions, without making it too easy (since the default return
|
exceptions, without making it too easy (since the default return
|
||||||
value, None, is false and this causes the exception to be
|
value, None, is false and this causes the exception to be
|
||||||
re-raised). The main use case for swallowing exceptions is to
|
re-raised). The main use case for swallowing exceptions is to
|
||||||
make it possible to write the @contextmanager decorator so thatn
|
make it possible to write the @contextmanager decorator so
|
||||||
that a try/except block in a decorated generator behaves exactly
|
that a try/except block in a decorated generator behaves exactly
|
||||||
as if the body of the generator were expanded in-line at the place
|
as if the body of the generator were expanded in-line at the place
|
||||||
of the with-statement.
|
of the with-statement.
|
||||||
|
@ -330,6 +338,17 @@ Specification: The 'with' Statement
|
||||||
methods should avoid raising errors unless they have actually
|
methods should avoid raising errors unless they have actually
|
||||||
failed. (And allowing the original error to proceed isn't a
|
failed. (And allowing the original error to proceed isn't a
|
||||||
failure.)
|
failure.)
|
||||||
|
|
||||||
|
Objects returned by __context__() methods should also provide a
|
||||||
|
__context__() method that returns self. This allows a program to
|
||||||
|
retrieve the context manager directly without breaking anything.
|
||||||
|
For example, the following should work just as well as the normal
|
||||||
|
case where the extra variable isn't used:
|
||||||
|
|
||||||
|
mgr = (EXPR).__context__()
|
||||||
|
with mgr as VAR:
|
||||||
|
BLOCK
|
||||||
|
|
||||||
|
|
||||||
Transition Plan
|
Transition Plan
|
||||||
|
|
||||||
|
@ -406,13 +425,13 @@ Generator Decorator
|
||||||
finally:
|
finally:
|
||||||
f.close() # Ditto for errors here (however unlikely)
|
f.close() # Ditto for errors here (however unlikely)
|
||||||
|
|
||||||
A robust builtin implementation of this decorator will be made
|
A robust implementation of this decorator will be made
|
||||||
part of the standard library.
|
part of the standard library.
|
||||||
|
|
||||||
Just as generator-iterator functions are very useful for writing
|
Just as generator-iterator functions are very useful for writing
|
||||||
__iter__() methods for iterables, generator-context functions will
|
__iter__() methods for iterables, generator context functions will
|
||||||
be very useful for writing __context__() methods for contexts.
|
be very useful for writing __context__() methods for context
|
||||||
These methods will still need to be decorated using the
|
objects. These methods will still need to be decorated using the
|
||||||
contextmanager decorator. To ensure an obvious error message if the
|
contextmanager decorator. To ensure an obvious error message if the
|
||||||
decorator is left out, generator-iterator objects will NOT be given
|
decorator is left out, generator-iterator objects will NOT be given
|
||||||
a native context - if you want to ensure a generator is closed
|
a native context - if you want to ensure a generator is closed
|
||||||
|
@ -446,7 +465,7 @@ Optional Extensions
|
||||||
is entered).
|
is entered).
|
||||||
|
|
||||||
OTOH such mistakes are easily diagnosed; for example, the
|
OTOH such mistakes are easily diagnosed; for example, the
|
||||||
generator-context decorator above raises RuntimeError when a
|
generator context decorator above raises RuntimeError when a
|
||||||
second with-statement calls f.__enter__() again. A similar error
|
second with-statement calls f.__enter__() again. A similar error
|
||||||
can be raised if __enter__ is invoked on a closed file object.
|
can be raised if __enter__ is invoked on a closed file object.
|
||||||
|
|
||||||
|
@ -470,19 +489,70 @@ Standard Terminology
|
||||||
returns an iterator (this means that all iterators are iterables,
|
returns an iterator (this means that all iterators are iterables,
|
||||||
but not all iterables are iterators).
|
but not all iterables are iterators).
|
||||||
|
|
||||||
This PEP proposes that the protocol used by the with statement be
|
This PEP proposes that the protocol consisting of the __enter__()
|
||||||
known as the "context management protocol", and that objects that
|
and __exit__() methods, and a __context__() method that returns
|
||||||
implement that protocol be known as "context managers". The term
|
self be known as the "context management protocol", and that
|
||||||
"context manager" then encompasses all objects with a __context__()
|
objects that implement that protocol be known as "context
|
||||||
method that returns a context object. (This means that all contexts
|
managers".
|
||||||
are context managers, but not all context managers are contexts).
|
|
||||||
|
|
||||||
The term "context" is based on the concept that the context object
|
The term "context object" then encompasses all objects with a
|
||||||
defines a context of execution for the code that forms the body
|
__context__() method that returns a context manager. The protocol
|
||||||
of the with statement.
|
these objects implement is called the "context protocol". This
|
||||||
|
means that all context managers are context objects, but not all
|
||||||
|
context objects are context managers, just as all iterators are
|
||||||
|
iterables, but not all iterables are iterators.
|
||||||
|
|
||||||
|
These terms are based on the concept that the context object
|
||||||
|
defines a context of execution for the code that forms the body of
|
||||||
|
the with statement. The role of the context manager is to
|
||||||
|
translate the context object's stored state into an active
|
||||||
|
manipulation of the runtime environment to setup and tear down the
|
||||||
|
desired runtime context for the duration of the with statement.
|
||||||
|
For example, a synchronisation lock's context manager acquires the
|
||||||
|
lock when entering the with statement, and releases the lock when
|
||||||
|
leaving it. The runtime context established within the body of the
|
||||||
|
with statement is that the synchronisation lock is currently held.
|
||||||
|
|
||||||
|
The general term "context" is unfortunately ambiguous. If necessary,
|
||||||
|
it can be made more explicit by using the terms "context objext" for
|
||||||
|
objects providing a __context__() method and "runtime context" for
|
||||||
|
the runtime environment modifications made by the context manager.
|
||||||
|
When solely discussing use of the with statement, the distinction
|
||||||
|
between the two shouldn't matter as the context object fully
|
||||||
|
defines the changes made to the runtime context. The distinction is
|
||||||
|
more important when discussing the process of implementing context
|
||||||
|
objects and context managers.
|
||||||
|
|
||||||
|
Open Issues
|
||||||
|
|
||||||
|
1. As noted earlier, the standard terminology section has not yet
|
||||||
|
met with consensus on python-dev. It will be refined throughout
|
||||||
|
the Python 2.5 release cycle based on user feedback on the
|
||||||
|
usability of the documentation.
|
||||||
|
|
||||||
|
2. The original resolution was for the decorator to make a context
|
||||||
|
manager from a generator to be a builtin called "contextmanager".
|
||||||
|
The shorter term "context" was considered too ambiguous and
|
||||||
|
potentially confusing [9].
|
||||||
|
The different flavours of generators could then be described as:
|
||||||
|
- A "generator function" is an undecorated function containing
|
||||||
|
the 'yield' keyword, and the objects produced by
|
||||||
|
such functions are "generator-iterators". The term
|
||||||
|
"generator" may refer to either a generator function or a
|
||||||
|
generator-iterator depending on the situation.
|
||||||
|
- A "generator context function" is a generator function to
|
||||||
|
which the "contextmanager" decorator is applied and the
|
||||||
|
objects produced by such functions are "generator-context-
|
||||||
|
managers". The term "generator context" may refer to either
|
||||||
|
a generator context function or a generator-context-manager
|
||||||
|
depending on the situation.
|
||||||
|
|
||||||
|
In the Python 2.5 implementation, the decorator is actually part
|
||||||
|
of the standard library module contextlib. The ongoing
|
||||||
|
terminology review may lead to it being renamed
|
||||||
|
"contextlib.context" (with the existence of the underlying context
|
||||||
|
manager being an implementation detail).
|
||||||
|
|
||||||
In cases where the general term "context" would be ambiguous, it
|
|
||||||
can be made explicit by expanding it to "manageable context".
|
|
||||||
|
|
||||||
Resolved Issues
|
Resolved Issues
|
||||||
|
|
||||||
|
@ -570,21 +640,7 @@ Resolved Issues
|
||||||
works without having to first understand the mechanics of
|
works without having to first understand the mechanics of
|
||||||
how generator context managers are implemented.
|
how generator context managers are implemented.
|
||||||
|
|
||||||
6. The decorator to make a context manager from a generator will be
|
6. See point 2 in open issues :)
|
||||||
a builtin called "contextmanager". The shorter term "context" was
|
|
||||||
considered too ambiguous and potentially confusing [9].
|
|
||||||
The different flavours of generators can then be described as:
|
|
||||||
- A "generator function" is an undecorated function containing
|
|
||||||
the 'yield' keyword, and the objects produced by
|
|
||||||
such functions are "generator-iterators". The term
|
|
||||||
"generator" may refer to either a generator function or a
|
|
||||||
generator-iterator depending on the situation.
|
|
||||||
- A "generator context function" is a generator function to
|
|
||||||
which the "contextmanager" decorator is applied and the
|
|
||||||
objects produced by such functions are "generator-context-
|
|
||||||
managers". The term "generator context" may refer to either a
|
|
||||||
generator context function or a generator-context-manager
|
|
||||||
depending on the situation.
|
|
||||||
|
|
||||||
7. A generator function used to implement a __context__ method will
|
7. A generator function used to implement a __context__ method will
|
||||||
need to be decorated with the contextmanager decorator in order
|
need to be decorated with the contextmanager decorator in order
|
||||||
|
@ -609,7 +665,7 @@ Examples
|
||||||
appropriate objects, such as threading.RLock, will be able to be
|
appropriate objects, such as threading.RLock, will be able to be
|
||||||
used directly in with statements.
|
used directly in with statements.
|
||||||
|
|
||||||
The tense used in the names of the example context managers is not
|
The tense used in the names of the example contexts is not
|
||||||
arbitrary. Past tense ("-ed") is used when the name refers to an
|
arbitrary. Past tense ("-ed") is used when the name refers to an
|
||||||
action which is done in the __enter__ method and undone in the
|
action which is done in the __enter__ method and undone in the
|
||||||
__exit__ method. Progressive tense ("-ing") is used when the name
|
__exit__ method. Progressive tense ("-ing") is used when the name
|
||||||
|
@ -634,7 +690,7 @@ Examples
|
||||||
# if via return or by an uncaught exception).
|
# if via return or by an uncaught exception).
|
||||||
|
|
||||||
PEP 319 gives a use case for also having an unlocked()
|
PEP 319 gives a use case for also having an unlocked()
|
||||||
template; this can be written very similarly (just swap the
|
context; this can be written very similarly (just swap the
|
||||||
acquire() and release() calls).
|
acquire() and release() calls).
|
||||||
|
|
||||||
2. A template for opening a file that ensures the file is closed
|
2. A template for opening a file that ensures the file is closed
|
||||||
|
@ -903,8 +959,10 @@ Examples
|
||||||
|
|
||||||
Reference Implementation
|
Reference Implementation
|
||||||
|
|
||||||
There is no implementation at this time. This PEP was accepted
|
This PEP was first accepted by Guido at his EuroPython
|
||||||
by Guido at his EuroPython keynote, 27 June 2005.
|
keynote, 27 June 2005.
|
||||||
|
It was accepted again later, with the __context__ method added.
|
||||||
|
The PEP was implemented for Python 2.5a1
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
|
@ -945,6 +1003,9 @@ References
|
||||||
[13]
|
[13]
|
||||||
http://mail.python.org/pipermail/python-dev/2006-February/061903.html
|
http://mail.python.org/pipermail/python-dev/2006-February/061903.html
|
||||||
|
|
||||||
|
[14]
|
||||||
|
http://mail.python.org/pipermail/python-dev/2006-April/063859.html
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
|
||||||
This document has been placed in the public domain.
|
This document has been placed in the public domain.
|
||||||
|
|
Loading…
Reference in New Issue