summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libdecimal.tex54
-rw-r--r--Lib/decimal.py65
-rw-r--r--Lib/test/test_decimal.py11
-rw-r--r--Misc/NEWS3
4 files changed, 45 insertions, 88 deletions
diff --git a/Doc/lib/libdecimal.tex b/Doc/lib/libdecimal.tex
index 3b41d6a..b6164fc 100644
--- a/Doc/lib/libdecimal.tex
+++ b/Doc/lib/libdecimal.tex
@@ -66,10 +66,8 @@ context for arithmetic, and signals.
A decimal number is immutable. It has a sign, coefficient digits, and an
exponent. To preserve significance, the coefficient digits do not truncate
trailing zeroes. Decimals also include special values such as
-\constant{Infinity} (the result of \samp{1 / 0}), \constant{-Infinity},
-(the result of \samp{-1 / 0}), and \constant{NaN} (the result of
-\samp{0 / 0}). The standard also differentiates \constant{-0} from
-\constant{+0}.
+\constant{Infinity}, \constant{-Infinity}, and \constant{NaN}. The standard
+also differentiates \constant{-0} from \constant{+0}.
The context for arithmetic is an environment specifying precision, rounding
rules, limits on exponents, flags that indicate the results of operations,
@@ -82,9 +80,7 @@ Signals are types of information that arise during the course of a
computation. Depending on the needs of the application, some signals may be
ignored, considered as informational, or treated as exceptions. The signals in
the decimal module are: \constant{Clamped}, \constant{InvalidOperation},
-\constant{ConversionSyntax}, \constant{DivisionByZero},
-\constant{DivisionImpossible}, \constant{DivisionUndefined},
-\constant{Inexact}, \constant{InvalidContext}, \constant{Rounded},
+\constant{DivisionByZero}, \constant{Inexact}, \constant{Rounded},
\constant{Subnormal}, \constant{Overflow}, and \constant{Underflow}.
For each signal there is a flag and a trap enabler. When a signal is
@@ -124,7 +120,7 @@ Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
Decimal instances can be constructed from integers, strings or tuples. To
create a Decimal from a \class{float}, first convert it to a string. This
serves as an explicit reminder of the details of the conversion (including
-representation error). Malformed strings signal \constant{ConversionSyntax}
+representation error). Malformed strings signal \constant{InvalidOperation}
and return a special kind of Decimal called a \constant{NaN} which stands for
``Not a number''. Positive and negative \constant{Infinity} is yet another
special kind of Decimal.
@@ -274,10 +270,8 @@ To turn all the traps on or off all at once, use a loop. Also, the
>>> getcontext().trap_enablers.update({Rounded:0, Inexact:0, Subnormal:0})
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
- setflags=[], settraps=['Underflow', 'DecimalException', 'Clamped',
- 'InvalidContext', 'InvalidOperation', 'ConversionSyntax',
- 'DivisionByZero', 'DivisionImpossible', 'DivisionUndefined',
- 'Overflow'])
+ setflags=[], settraps=['Clamped', 'Underflow', 'InvalidOperation',
+ 'DivisionByZero', 'Overflow'])
\end{verbatim}
Applications typically set the context once at the beginning of a program
@@ -320,7 +314,7 @@ as other Python numeric types.
The supplied \var{context} or, if not specified, the current context
governs only the handling of malformed strings not conforming to the
- numeric string syntax. If the context traps \constant{ConversionSyntax},
+ numeric string syntax. If the context traps \constant{InvalidOperation},
an exception is raised; otherwise, the constructor returns a new Decimal
with the value of \constant{NaN}.
@@ -730,13 +724,6 @@ exception is raised upon encountering the condition.
reduced to fit by adding zeroes to the coefficient.
\end{classdesc*}
-\begin{classdesc*}{ConversionSyntax}
- Trying to convert a malformed string such as: \code{Decimal('jump')}.
-
- Decimal converts only strings conforming to the numeric string
- syntax. If this signal is not trapped, returns \constant{NaN}.
-\end{classdesc*}
-
\begin{classdesc*}{DecimalException}
Base class for other signals.
\end{classdesc*}
@@ -750,19 +737,6 @@ exception is raised upon encountering the condition.
the inputs to the calculation.
\end{classdesc*}
-\begin{classdesc*}{DivisionImpossible}
- Error performing a division operation. Caused when an intermediate result
- has more digits that the allowed by the current precision. If not trapped,
- returns \constant{NaN}.
-\end{classdesc*}
-
-
-\begin{classdesc*}{DivisionUndefined}
- This is a subclass of \class{DivisionByZero}.
-
- It occurs only in the context of division operations.
-\end{classdesc*}
-
\begin{classdesc*}{Inexact}
Indicates that rounding occurred and the result is not exact.
@@ -771,14 +745,6 @@ exception is raised upon encountering the condition.
to detect when results are inexact.
\end{classdesc*}
-
-\begin{classdesc*}{InvalidContext}
- This is a subclass of \class{InvalidOperation}.
-
- Indicates an error within the Context object such as an unknown
- rounding operation. If not trapped, returns \constant{NaN}.
-\end{classdesc*}
-
\begin{classdesc*}{InvalidOperation}
An invalid operation was performed.
@@ -809,7 +775,6 @@ exception is raised upon encountering the condition.
\class{Rounded} are also signaled.
\end{classdesc*}
-
\begin{classdesc*}{Rounded}
Rounding occurred though possibly no information was lost.
@@ -844,16 +809,11 @@ The following table summarizes the hierarchy of signals:
Overflow(Inexact, Rounded)
Underflow(Inexact, Rounded, Subnormal)
InvalidOperation
- ConversionSyntax
- DivisionImpossible
- DivisionUndefined(InvalidOperation, exceptions.ZeroDivisionError)
- InvalidContext
Rounded
Subnormal
\end{verbatim}
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Working with threads \label{decimal-threads}}
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 4e5cbbb..9f77b24 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -78,30 +78,30 @@ Traceback (most recent call last):
...
DivisionByZero: x / 0
>>> c = Context()
->>> c.trap_enablers[DivisionUndefined] = 0
->>> print c.flags[DivisionUndefined]
+>>> c.trap_enablers[InvalidOperation] = 0
+>>> print c.flags[InvalidOperation]
0
>>> c.divide(Decimal(0), Decimal(0))
Decimal("NaN")
->>> c.trap_enablers[DivisionUndefined] = 1
->>> print c.flags[DivisionUndefined]
+>>> c.trap_enablers[InvalidOperation] = 1
+>>> print c.flags[InvalidOperation]
1
->>> c.flags[DivisionUndefined] = 0
->>> print c.flags[DivisionUndefined]
+>>> c.flags[InvalidOperation] = 0
+>>> print c.flags[InvalidOperation]
0
>>> print c.divide(Decimal(0), Decimal(0))
Traceback (most recent call last):
...
...
...
-DivisionUndefined: 0 / 0
->>> print c.flags[DivisionUndefined]
+InvalidOperation: 0 / 0
+>>> print c.flags[InvalidOperation]
1
->>> c.flags[DivisionUndefined] = 0
->>> c.trap_enablers[DivisionUndefined] = False
+>>> c.flags[InvalidOperation] = 0
+>>> c.trap_enablers[InvalidOperation] = 0
>>> print c.divide(Decimal(0), Decimal(0))
NaN
->>> print c.flags[DivisionUndefined]
+>>> print c.flags[InvalidOperation]
1
>>>
"""
@@ -152,7 +152,7 @@ ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end.
#Errors
class DecimalException(ArithmeticError):
- """Base exception class, defines default things.
+ """Base exception class.
Used exceptions derive from this.
If an exception derives from another exception besides this (such as
@@ -160,12 +160,6 @@ class DecimalException(ArithmeticError):
called if the others are present. This isn't actually used for
anything, though.
- Attributes:
-
- default -- If the context is basic, the trap_enablers are set to
- this by default. Extended contexts start out with them set
- to 0, regardless.
-
handle -- Called when context._raise_error is called and the
trap_enabler is set. First argument is self, second is the
context. More arguments can be given, those being after
@@ -176,7 +170,6 @@ class DecimalException(ArithmeticError):
To define a new exception, it should be sufficient to have it derive
from DecimalException.
"""
- default = 1
def handle(self, context, *args):
pass
@@ -288,7 +281,7 @@ class Inexact(DecimalException):
The inexact signal may be tested (or trapped) to determine if a given
operation (or sequence of operations) was inexact.
"""
- default = 0
+ pass
class InvalidContext(InvalidOperation):
"""Invalid context. Unknown rounding, for example.
@@ -315,7 +308,7 @@ class Rounded(DecimalException):
The rounded signal may be tested (or trapped) to determine if a given
operation (or sequence of operations) caused a loss of precision.
"""
- default = 0
+ pass
class Subnormal(DecimalException):
"""Exponent < Emin before rounding.
@@ -382,19 +375,15 @@ class Underflow(Inexact, Rounded, Subnormal):
In all cases, Inexact, Rounded, and Subnormal will also be raised.
"""
+# List of public traps and flags
+Signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded,
+ Underflow, InvalidOperation, Subnormal]
-def _filterfunc(obj):
- """Returns true if a subclass of DecimalException"""
- try:
- return issubclass(obj, DecimalException)
- except TypeError:
- return False
-
-#Signals holds the exceptions
-Signals = filter(_filterfunc, globals().values())
-
-del _filterfunc
-
+# Map conditions (per the spec) to signals
+_condition_map = {ConversionSyntax:InvalidOperation,
+ DivisionImpossible:InvalidOperation,
+ DivisionUndefined:InvalidOperation,
+ InvalidContext:InvalidOperation}
##### Context Functions #######################################
@@ -2168,7 +2157,7 @@ class Context(object):
return nc
__copy__ = copy
- def _raise_error(self, error, explanation = None, *args):
+ def _raise_error(self, condition, explanation = None, *args):
"""Handles an error
If the flag is in _ignored_flags, returns the default response.
@@ -2176,6 +2165,7 @@ class Context(object):
trap_enabler is set, it reaises the exception. Otherwise, it returns
the default value after incrementing the flag.
"""
+ error = _condition_map.get(condition, condition)
if error in self._ignored_flags:
#Don't touch the flag
return error().handle(self, *args)
@@ -2183,7 +2173,7 @@ class Context(object):
self.flags[error] += 1
if not self.trap_enablers[error]:
#The errors define how to handle themselves.
- return error().handle(self, *args)
+ return condition().handle(self, *args)
# Errors should only be risked on copies of the context
#self._ignored_flags = []
@@ -2207,6 +2197,11 @@ class Context(object):
for flag in flags:
self._ignored_flags.remove(flag)
+ def __hash__(self):
+ """A Context cannot be hashed."""
+ # We inherit object.__hash__, so we must deny this explicitly
+ raise TypeError, "Cannot hash a Context."
+
def Etiny(self):
"""Returns Etiny (= Emin - prec + 1)"""
return int(self.Emin - self.prec + 1)
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index a03b784..e6d9389 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -60,12 +60,12 @@ EXTENDEDERRORTEST = False
#Map the test cases' error names to the actual errors
ErrorNames = {'clamped' : Clamped,
- 'conversion_syntax' : ConversionSyntax,
+ 'conversion_syntax' : InvalidOperation,
'division_by_zero' : DivisionByZero,
- 'division_impossible' : DivisionImpossible,
- 'division_undefined' : DivisionUndefined,
+ 'division_impossible' : InvalidOperation,
+ 'division_undefined' : InvalidOperation,
'inexact' : Inexact,
- 'invalid_context' : InvalidContext,
+ 'invalid_context' : InvalidOperation,
'invalid_operation' : InvalidOperation,
'overflow' : Overflow,
'rounded' : Rounded,
@@ -131,6 +131,7 @@ class DecimalTest(unittest.TestCase):
return
for line in open(file).xreadlines():
line = line.replace('\r\n', '').replace('\n', '')
+ #print line
try:
t = self.eval_line(line)
except ConversionSyntax:
@@ -648,7 +649,6 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase):
self.assertEqual(d1, Decimal('-0.625'))
def test_floor_division(self):
- '''Test floor division in all its ways.'''
d1 = Decimal('5')
d2 = Decimal('2')
@@ -676,7 +676,6 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase):
self.assertEqual(d1, Decimal('1'))
def test_powering(self):
- '''Test powering in all its ways.'''
d1 = Decimal('5')
d2 = Decimal('2')
diff --git a/Misc/NEWS b/Misc/NEWS
index df3dcca..b7a1ade 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -26,6 +26,9 @@ Extension modules
Library
-------
+- decimal.py now only uses signals in the spec. The other conditions are
+ no longer part of the public API.
+
Tools/Demos
-----------