[Python-checkins] r51695 - in python/branches/release25-maint: Doc/lib/libdecimal.tex Doc/whatsnew/whatsnew25.tex Lib/decimal.py Lib/test/test_contextlib.py Lib/test/test_decimal.py

nick.coghlan python-checkins at python.org
Sun Sep 3 03:08:31 CEST 2006


Author: nick.coghlan
Date: Sun Sep 3 03:08:30 2006
New Revision: 51695
Modified:
 python/branches/release25-maint/Doc/lib/libdecimal.tex
 python/branches/release25-maint/Doc/whatsnew/whatsnew25.tex
 python/branches/release25-maint/Lib/decimal.py
 python/branches/release25-maint/Lib/test/test_contextlib.py
 python/branches/release25-maint/Lib/test/test_decimal.py
Log:
Backport of decimal module context management updates from rev 51694 to 2.5 release branch
Modified: python/branches/release25-maint/Doc/lib/libdecimal.tex
==============================================================================
--- python/branches/release25-maint/Doc/lib/libdecimal.tex	(original)
+++ python/branches/release25-maint/Doc/lib/libdecimal.tex	Sun Sep 3 03:08:30 2006
@@ -435,36 +435,37 @@
 the \function{getcontext()} and \function{setcontext()} functions:
 
 \begin{funcdesc}{getcontext}{}
- Return the current context for the active thread. 
+ Return the current context for the active thread.
 \end{funcdesc} 
 
 \begin{funcdesc}{setcontext}{c}
- Set the current context for the active thread to \var{c}. 
+ Set the current context for the active thread to \var{c}.
 \end{funcdesc} 
 
 Beginning with Python 2.5, you can also use the \keyword{with} statement
-to temporarily change the active context. For example the following code
-increases the current decimal precision by 2 places, performs a
-calculation, and then automatically restores the previous context:
+and the \function{localcontext()} function to temporarily change the
+active context.
 
-\begin{verbatim}
-from __future__ import with_statement
-import decimal
-
-with decimal.getcontext() as ctx:
- ctx.prec += 2 # add 2 more digits of precision
- calculate_something()
+\begin{funcdesc}{localcontext}{\optional{c}}
+ Return a context manager that will set the current context for
+ the active thread to a copy of \var{c} on entry to the with-statement
+ and restore the previous context when exiting the with-statement. If
+ no context is specified, a copy of the current context is used.
+ \versionadded{2.5}
+
+ For example, the following code sets the current decimal precision
+ to 42 places, performs a calculation, and then automatically restores
+ the previous context:
+\begin{verbatim}
+ from __future__ import with_statement
+ from decimal import localcontext
+
+ with localcontext() as ctx:
+ ctx.prec = 42 # Perform a high precision calculation
+ s = calculate_something()
+ s = +s # Round the final result back to the default precision
 \end{verbatim}
-
-The context that's active in the body of the \keyword{with} statement is
-a \emph{copy} of the context you provided to the \keyword{with}
-statement, so modifying its attributes doesn't affect anything except
-that temporary copy.
-
-You can use any decimal context in a \keyword{with} statement, but if
-you just want to make a temporary change to some aspect of the current
-context, it's easiest to just use \function{getcontext()} as shown
-above.
+\end{funcdesc}
 
 New contexts can also be created using the \class{Context} constructor
 described below. In addition, the module provides three pre-made
Modified: python/branches/release25-maint/Doc/whatsnew/whatsnew25.tex
==============================================================================
--- python/branches/release25-maint/Doc/whatsnew/whatsnew25.tex	(original)
+++ python/branches/release25-maint/Doc/whatsnew/whatsnew25.tex	Sun Sep 3 03:08:30 2006
@@ -683,22 +683,22 @@
 The lock is acquired before the block is executed and always released once 
 the block is complete.
 
-The \module{decimal} module's contexts, which encapsulate the desired
-precision and rounding characteristics for computations, provide a 
-\method{context_manager()} method for getting a context manager:
+The new \function{localcontext()} function in the \module{decimal} module
+makes it easy to save and restore the current decimal context, which
+encapsulates the desired precision and rounding characteristics for
+computations:
 
 \begin{verbatim}
-import decimal
+from decimal import Decimal, Context, localcontext
 
 # Displays with default precision of 28 digits
-v1 = decimal.Decimal('578')
-print v1.sqrt()
+v = Decimal('578')
+print v.sqrt()
 
-ctx = decimal.Context(prec=16) 
-with ctx.context_manager():
+with localcontext(Context(prec=16)):
 # All code in this block uses a precision of 16 digits.
 # The original context is restored on exiting the block.
- print v1.sqrt()
+ print v.sqrt()
 \end{verbatim}
 
 \subsection{Writing Context Managers\label{context-managers}}
Modified: python/branches/release25-maint/Lib/decimal.py
==============================================================================
--- python/branches/release25-maint/Lib/decimal.py	(original)
+++ python/branches/release25-maint/Lib/decimal.py	Sun Sep 3 03:08:30 2006
@@ -131,7 +131,7 @@
 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN',
 
 # Functions for manipulating contexts
- 'setcontext', 'getcontext'
+ 'setcontext', 'getcontext', 'localcontext'
 ]
 
 import copy as _copy
@@ -458,6 +458,49 @@
 
 del threading, local # Don't contaminate the namespace
 
+def localcontext(ctx=None):
+ """Return a context manager for a copy of the supplied context
+
+ Uses a copy of the current context if no context is specified
+ The returned context manager creates a local decimal context
+ in a with statement:
+ def sin(x):
+ with localcontext() 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 localcontext(ExtendedContext):
+ # Rest of sin calculation algorithm
+ # uses the Extended Context from the
+ # General Decimal Arithmetic Specification
+ return +s # Convert result to normal context
+
+ """
+ # The below can't be included in the docstring until Python 2.6
+ # as the doctest module doesn't understand __future__ statements
+ """
+ >>> from __future__ import with_statement
+ >>> print getcontext().prec
+ 28
+ >>> with localcontext():
+ ... ctx = getcontext()
+ ... ctx.prec() += 2
+ ... print ctx.prec
+ ...
+ 30
+ >>> with localcontext(ExtendedContext):
+ ... print getcontext().prec
+ ...
+ 9
+ >>> print getcontext().prec
+ 28
+ """
+ if ctx is None: ctx = getcontext()
+ return _ContextManager(ctx)
+
 
 ##### Decimal class ###########################################
 
@@ -2173,23 +2216,14 @@
 
 del name, val, globalname, rounding_functions
 
-class ContextManager(object):
- """Helper class to simplify Context management.
-
- Sample usage:
-
- with decimal.ExtendedContext:
- s = ...
- return +s # Convert result to normal precision
-
- with decimal.getcontext() as ctx:
- ctx.prec += 2
- s = ...
- return +s
+class _ContextManager(object):
+ """Context manager class to support localcontext().
 
+ Sets a copy of the supplied context in __enter__() and restores
+ the previous decimal context in __exit__()
 """
 def __init__(self, new_context):
- self.new_context = new_context
+ self.new_context = new_context.copy()
 def __enter__(self):
 self.saved_context = getcontext()
 setcontext(self.new_context)
@@ -2248,9 +2282,6 @@
 s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']')
 return ', '.join(s) + ')'
 
- def get_manager(self):
- return ContextManager(self.copy())
-
 def clear_flags(self):
 """Reset all flags to zero"""
 for flag in self.flags:
Modified: python/branches/release25-maint/Lib/test/test_contextlib.py
==============================================================================
--- python/branches/release25-maint/Lib/test/test_contextlib.py	(original)
+++ python/branches/release25-maint/Lib/test/test_contextlib.py	Sun Sep 3 03:08:30 2006
@@ -330,32 +330,6 @@
 return True
 self.boilerPlate(lock, locked)
 
-class DecimalContextTestCase(unittest.TestCase):
-
- # XXX Somebody should write more thorough tests for this
-
- def testBasic(self):
- ctx = decimal.getcontext()
- orig_context = ctx.copy()
- try:
- ctx.prec = save_prec = decimal.ExtendedContext.prec + 5
- with decimal.ExtendedContext.get_manager():
- self.assertEqual(decimal.getcontext().prec,
- decimal.ExtendedContext.prec)
- self.assertEqual(decimal.getcontext().prec, save_prec)
- try:
- with decimal.ExtendedContext.get_manager():
- self.assertEqual(decimal.getcontext().prec,
- decimal.ExtendedContext.prec)
- 1/0
- except ZeroDivisionError:
- self.assertEqual(decimal.getcontext().prec, save_prec)
- else:
- self.fail("Didn't raise ZeroDivisionError")
- finally:
- decimal.setcontext(orig_context)
-
-
 # This is needed to make the test actually run under regrtest.py!
 def test_main():
 run_suite(
Modified: python/branches/release25-maint/Lib/test/test_decimal.py
==============================================================================
--- python/branches/release25-maint/Lib/test/test_decimal.py	(original)
+++ python/branches/release25-maint/Lib/test/test_decimal.py	Sun Sep 3 03:08:30 2006
@@ -23,6 +23,7 @@
 you're working through IDLE, you can import this test module and call test_main()
 with the corresponding argument.
 """
+from __future__ import with_statement
 
 import unittest
 import glob
@@ -1064,6 +1065,32 @@
 self.assertNotEqual(id(c.flags), id(d.flags))
 self.assertNotEqual(id(c.traps), id(d.traps))
 
+class WithStatementTest(unittest.TestCase):
+ # Can't do these as docstrings until Python 2.6
+ # as doctest can't handle __future__ statements
+
+ def test_localcontext(self):
+ # Use a copy of the current context in the block
+ orig_ctx = getcontext()
+ with localcontext() as enter_ctx:
+ set_ctx = getcontext()
+ final_ctx = getcontext()
+ self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
+ self.assert_(orig_ctx is not set_ctx, 'did not copy the context')
+ self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
+
+ def test_localcontextarg(self):
+ # Use a copy of the supplied context in the block
+ orig_ctx = getcontext()
+ new_ctx = Context(prec=42)
+ with localcontext(new_ctx) as enter_ctx:
+ set_ctx = getcontext()
+ final_ctx = getcontext()
+ self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
+ self.assert_(set_ctx.prec == new_ctx.prec, 'did not set correct context')
+ self.assert_(new_ctx is not set_ctx, 'did not copy the context')
+ self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
+
 def test_main(arith=False, verbose=None):
 """ Execute the tests.
 
@@ -1084,6 +1111,7 @@
 DecimalPythonAPItests,
 ContextAPItests,
 DecimalTest,
+ WithStatementTest,
 ]
 
 try:


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /