[Python-Dev] Proposed changes to PEP 343
Nick Coghlan
ncoghlan at iinet.net.au
Fri Oct 7 11:57:17 CEST 2005
Based on Jason's comments regarding decimal.Context, and to explicitly cover
the terminology agreed on during the documentation discussion back in July,
I'm proposing a number of changes to PEP 343. I'll be updating the checked in
PEP assuming there aren't any objections in the next week or so (and assuming
I get CVS access sorted out ;).
The idea of dropping __enter__/__exit__ and defining the with statement solely
in terms of coroutines is *not* included in the suggested changes, but I added
a new item under "Resolved Open Issues" to cover some of the reasons why.
Cheers,
Nick.
1. Amend the statement specification such that:
with EXPR as VAR:
BLOCK
is translated as:
abc = (EXPR).__with__()
exc = (None, None, None)
VAR = abc.__enter__()
try:
try:
BLOCK
except:
exc = sys.exc_info()
raise
finally:
abc.__exit__(*exc)
2. Add the following to the subsequent explanation:
The call to the __with__ method serves a similar purpose to the __iter__
method for iterables and iterators. An object such as threading.Lock may
provide its own __enter__ and __exit__ methods, and simply return 'self'
from its __with__ method. A more complex object such as decimal.Context may
return a distinct context manager which takes care of setting and restoring
the appropriate decimal context in the thread.
3. Update ContextWrapper in the "Generator Decorator" section to include:
def __with__(self):
return self
4. Add a paragraph to the end of the "Generator Decorator" section:
By applying the @contextmanager decorator to a context's __with__ method,
it is as easy to write a generator-based context manager for the context as
it is to write a generator-based iterator for an iterable (see the
decimal.Context example below).
5. Add three items under "Resolved Open Issues":
2. After this PEP was originally approved, a subsequent discussion on
python-dev [4] settled on the term "context manager" for objects which
provide __enter__ and __exit__ methods, and "context management
protocol" for the protocol itself. With the addition of the __with__
method to the protocol, a natural extension is to call objects which
provide only a __with__ method "contexts" (or "manageable contexts" in
situations where the general term "context" would be ambiguous).
The distinction between a context and a context manager is very
similar to the distinction between an iterable and an iterator.
3. The originally approved version of this PEP did not include a __with__
method - the method was only added to the PEP after Jason Orendorff
pointed out the difficulty of writing appropriate __enter__ and __exit__
methods for decimal.Context [5].
This approach allows a class to use the @contextmanager decorator
to defines a native context manager using generator syntax. It also
allows a class to use an existing independent context manager as its
native context manager by applying the independent context manager to
'self' in its __with__ method. It even allows a class written in C to
use a coroutine based context manager written in Python.
The __with__ method parallels the __iter__ method which forms part of
the iterator protocol.
4. The suggestion was made by Jason Orendorff that the __enter__ and
__exit__ methods could be removed from the context management protocol,
and the protocol instead defined directly in terms of the coroutine
interface described in PEP 342 (or a cleaner version of that interface
with start() and finish() convenience methods) [6].
Guido rejected this idea [7]. The following are some of benefits of
keeping the __enter__ and __exit__ methods:
- it makes it easy to implement a simple context manager in C
without having to rely on a separate coroutine builder
- it makes it easy to provide a low-overhead implementation for
context managers which don't need to maintain any special state
between the __enter__ and __exit__ methods (having to use a
coroutine for these would impose unnecessary overhead without any
compensating benefit)
- it makes it possible to understand how the with statement works
without having to first understand the concept of a coroutine
6. Add new references:
[4] http://mail.python.org/pipermail/python-dev/2005-July/054658.html
[5] http://mail.python.org/pipermail/python-dev/2005-October/056947.html
[6] http://mail.python.org/pipermail/python-dev/2005-October/056969.html
[7] http://mail.python.org/pipermail/python-dev/2005-October/057018.html
7. Update Example 4 to include a __with__ method:
def __with__(self):
return self
8. Replace Example 9 with the following example:
9. Here's a proposed native context manager for decimal.Context:
# This would be a new decimal.Context method
@contextmanager
def __with__(self):
# We set the thread context to a copy of this context
# to ensure that changes within the block are kept
# local to the block. This also gives us thread safety
# and supports nested usage of a given context.
newctx = self.copy()
oldctx = decimal.getcontext()
decimal.setcontext(newctx)
try:
yield newctx
finally:
decimal.setcontext(oldctx)
Sample usage:
def sin(x):
with decimal.getcontext() as ctx:
ctx.prec += 2
# Rest of sin calculation algorithm
# uses a precision 2 greater than normal
return +s # Convert result to normal precision
def sin(x):
with decimal.ExtendedContext:
# Rest of sin calculation algorithm
# uses the Extended Context from the
# General Decimal Arithmetic Specification
return +s # Convert result to normal context
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.blogspot.com
More information about the Python-Dev
mailing list