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:
Nick Coghlan 2006-04-23 05:14:10 +00:00
parent f554d12d6f
commit db32145d0e
1 changed files with 113 additions and 52 deletions

View File

@ -7,7 +7,7 @@ Status: Accepted
Type: Standards Track
Content-Type: text/plain
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
@ -19,6 +19,7 @@ Abstract
section on Resolved Issues). It's still at Draft status until
Guido gives a final blessing to the updated PEP.
Author's Note
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
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
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:
ctx = (EXPR).__context__()
exit = ctx.__exit__ # Not calling it yet
value = ctx.__enter__()
mgr = (EXPR).__context__()
exit = mgr.__exit__ # Not calling it yet
value = mgr.__enter__()
exc = True
try:
try:
@ -244,7 +252,7 @@ Specification: The 'with' Statement
if exc:
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
implemented as special registers or stack positions.
@ -260,37 +268,37 @@ Specification: The 'with' Statement
as exceptions by __exit__().)
The call to the __context__() method serves a similar purpose to
that of the __iter__() method of iterator and iterables. An
object with with simple state requirements (such as
that of the __iter__() method of iterator and iterables. A context
object with simple state requirements (such as
threading.RLock) may provide its own __enter__() and __exit__()
methods, and simply return 'self' from its __context__ method. On
the other hand, an object with more complex state requirements
(such as decimal.Context) may return a distinct context object
the other hand, a context object with more complex state requirements
(such as decimal.Context) may return a distinct context manager
each time its __context__ method is invoked.
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
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
BLOCK, ctx.__exit__() is called with three arguments representing
BLOCK, mgr.__exit__() is called with three arguments representing
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
continues at the next statement after the with-statement, even if
an exception happened inside the with-statement. However, if the
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
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
value, None, is false and this causes the exception to be
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
as if the body of the generator were expanded in-line at the place
of the with-statement.
@ -330,6 +338,17 @@ Specification: The 'with' Statement
methods should avoid raising errors unless they have actually
failed. (And allowing the original error to proceed isn't a
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
@ -406,13 +425,13 @@ Generator Decorator
finally:
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.
Just as generator-iterator functions are very useful for writing
__iter__() methods for iterables, generator-context functions will
be very useful for writing __context__() methods for contexts.
These methods will still need to be decorated using the
__iter__() methods for iterables, generator context functions will
be very useful for writing __context__() methods for context
objects. These methods will still need to be decorated using the
contextmanager decorator. To ensure an obvious error message if the
decorator is left out, generator-iterator objects will NOT be given
a native context - if you want to ensure a generator is closed
@ -446,7 +465,7 @@ Optional Extensions
is entered).
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
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,
but not all iterables are iterators).
This PEP proposes that the protocol used by the with statement be
known as the "context management protocol", and that objects that
implement that protocol be known as "context managers". The term
"context manager" then encompasses all objects with a __context__()
method that returns a context object. (This means that all contexts
are context managers, but not all context managers are contexts).
This PEP proposes that the protocol consisting of the __enter__()
and __exit__() methods, and a __context__() method that returns
self be known as the "context management protocol", and that
objects that implement that protocol be known as "context
managers".
The term "context" is 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 term "context object" then encompasses all objects with a
__context__() method that returns a context manager. The protocol
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
@ -570,21 +640,7 @@ Resolved Issues
works without having to first understand the mechanics of
how generator context managers are implemented.
6. The decorator to make a context manager from a generator will be
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.
6. See point 2 in open issues :)
7. A generator function used to implement a __context__ method will
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
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
action which is done in the __enter__ method and undone in the
__exit__ method. Progressive tense ("-ing") is used when the name
@ -634,7 +690,7 @@ Examples
# if via return or by an uncaught exception).
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).
2. A template for opening a file that ensures the file is closed
@ -903,8 +959,10 @@ Examples
Reference Implementation
There is no implementation at this time. This PEP was accepted
by Guido at his EuroPython keynote, 27 June 2005.
This PEP was first accepted by Guido at his EuroPython
keynote, 27 June 2005.
It was accepted again later, with the __context__ method added.
The PEP was implemented for Python 2.5a1
References
@ -945,6 +1003,9 @@ References
[13]
http://mail.python.org/pipermail/python-dev/2006-February/061903.html
[14]
http://mail.python.org/pipermail/python-dev/2006-April/063859.html
Copyright
This document has been placed in the public domain.