Module and tests:

* Map conditions to related signals.
* Make contexts unhashable.
* Eliminate used "default" attribute in exception definitions.
* Eliminate the _filterfunc in favor of a straight list.

Docs:
* Eliminate documented references to conditions that are not signals.
* Eliminate parenthetical notes such as "1/0 --> Inf" which are no
  longer true with the new defaults.
This commit is contained in:
Raymond Hettinger 2004-07-09 10:02:53 +00:00
parent 563e449729
commit 5aa478badf
4 changed files with 45 additions and 88 deletions

View file

@ -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)