[Python-Dev] Py2.5 issue: decimal context manager misimplemented, misdesigned, and misdocumented
Nick Coghlan
ncoghlan at gmail.com
Wed Aug 30 13:11:52 CEST 2006
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 at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list