[Python-Dev] PEP 343 and __context__()

Nick Coghlan ncoghlan at gmail.com
Fri Jan 20 10:21:43 CET 2006


Jason Orendorff wrote:
> I just noticed that my name is in PEP 343 attached to the idea of the
> __context__() method, and I'm slightly queasy over it.
> 
> The rationale was to help e.g. decimal.DecimalContext support 'with'. 
> Maybe that's a bad idea.
> 
> DecimalContext has a few problems.  In code where it matters, every
> function you write has to worry about it. (That is, you can't just
> write __decimal_context__ = ... at the top of the file and be done
> with it, the way you can with, say, __metaclass__.)

No, you write "decimal.setcontext(...)" instead. You then only need to worry 
if you have multiple threads, and you can even fix that by modifying 
decimal.DefaultContext while the program is still single threaded. The latter 
approach actually works even for a single-threaded program, but should only be 
done in an application script, never in an imported module.

>  And
> DecimalContext doesn't fit in with generators.

It does fit actually - you simply have to remember to restore the original 
context around any invocations of yield.

> sys.stdout has similar problems.
> 
> It feels like PEP 343 will make people want to follow this model. 
> That is, we'll see more global behavior-controlling variables in the
> future.  There are grizzlier fates; I just wondered if anyone had
> thought of this.

Yeah, it came up in response to PJE's suggestion of task-local variables for 
generators. The basic concept we came up with is that if you're writing a 
generator that uses a specific context, remember to save the original and 
restore it around any calls to yield (at least, I came up with the idea and 
no-one objected to the approach [1]).

In the case of decimal.Context:

      def iter_sin(iterable):
         orig_ctx = decimal.getcontext()
         with orig_ctx as ctx:
             ctx.prec += 10
             for r in iterable:
                 y = sin(r) # Very high precision during calculation
                 with orig_ctx:
                     yield +y # Yielded results have normal precision
                 # We get "ctx" back here
         # We get "orig_ctx" back here

Similarly for sys.stdout (only not thread-safe due to the global state):

      def log_yielded_items(iterable, logfile):
         orig_stdout = sys.stdout
         with redirected_stdout(logfile):
             for r in iterable:
                 print "Yielding:", r
                 with redirected_stdout(orig_stdout):
                     yield r # stdout goes back to normal here
                 # stdout is going to the logfile again here
         # And stdout is back to normal at the end


The key properties of a "well-behaved" context are:
   1. Use thread-local state for contexts in order to be multithreading safe
     decimal.Context obeys this, sys.stdout doesn't

   2. Provide a way to retrieve the current state for explicit restoration
     decimal.getcontext() and sys.stdout allow this as shown above

This kind of thing is always going to be a pain, but PEP 343 makes it 
significantly less so.

Cheers,
Nick.

[1] http://mail.python.org/pipermail/python-dev/2005-October/057493.html

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://www.boredomandlaziness.org


More information about the Python-Dev mailing list