[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