![](https://secure.gravatar.com/avatar/f3ba3ecffd20251d73749afbfa636786.jpg?s=120&d=mm&r=g)
Raymond Hettinger wrote:
I would like to see the changes to the decimal module reverted for the Py2.5 release.
I believe you may be overreacting - I don't consider the current behaviour buggy and the module level API can be added later. That said, the docstring is definitely wrong, and I can't find any unit tests for the feature (I thought there were some in test_with, but I appear to be mistaken).
Currently, the code in the decimal module implements the context manager as a separate class instead of incorporating it directly in decimal.Context. This makes the API unnecessarily complex and is not pretty compared to the code it was intended to replace.
The removal of the __context__ method made it impossible to permit context objects to be used directly in with statements. Even when that was the case, the separate ContextManager class was necessary in order to correctly handle the restoration as context objects may be shared between threads or nested within a single thread [1]. The localcontext() function in PEP 343 does exactly the same thing - it merely uses a generator context instead of a direct implementation of __enter__ and __exit__. The current syntax (the get_manager() method) can easily be made prettier in the future by adding a sugar function at the module level: def localcontext(ctx=None): if ctx is None: ctx = getcontext() return ctx.get_manager()
Worse still, the implementation saves a reference to the context instead of making a copy of it. Remember decimal.Context objects are mutable -- the current implementation does not fulfill its contract to restore the context to its original state at the conclusion of the with-statement.
The implementation doesn't forget that. The context to restore is determined by calling getcontext() in the __enter__ method. The restored context has nothing to do with the context passed to the ContextManager constructor.
from decimal import getcontext() getcontext().prec 28 with getcontext().get_manager() as ctx: ... ctx.prec += 2 ... getcontext().prec 28
The only ways to break it are to call ContextManager directly with an existing context that someone else already has a reference to:
from decimal import getcontext() getcontext().prec 28 with ContextManager(getcontext()) as ctx: ... ctx.prec += 2 ... getcontext().prec 30
Or to deliberately reuse a ContextManager instance:
mgr = getcontext().get_manager() with mgr as ctx: ... ctx.prec += 2 ... print ctx.prec ... 32 with mgr as ctx: ... ctx.prec += 2 ... print ctx.prec ... 34
Cheers, Nick. [1] In fact, get_manager() is merely a new name for the old __context__ method. This name was suggested by Neal after I initially called the method context_manager(), but was never separately discussed on python-dev (the original discussion occurred in one of the massive PEP 343 threads). http://mail.python.org/pipermail/python-checkins/2006-May/052083.html -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org