--- a/Lib/decimal.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/decimal.py Wed Dec 14 18:48:33 2011 +0100 @@ -46,8 +46,8 @@ Decimal('-0.0123') >>> Decimal(123456) Decimal('123456') ->>> Decimal('123.45e12345678901234567890') -Decimal('1.2345E+12345678901234567892') +>>> Decimal('123.45e12345678') +Decimal('1.2345E+12345680') >>> Decimal('1.33') + Decimal('1.27') Decimal('2.60') >>> Decimal('12.34') + Decimal('3.87') - Decimal('18.41') @@ -380,6 +380,10 @@ DivisionUndefined:InvalidOperation, InvalidContext:InvalidOperation} +# Valid rounding modes +_rounding_modes = (ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_CEILING, + ROUND_FLOOR, ROUND_UP, ROUND_HALF_DOWN, ROUND_05UP) + ##### Context Functions ################################################## # The getcontext() and setcontext() function manage access to a thread-local @@ -684,7 +688,9 @@ """ if isinstance(f, int): # handle integer inputs return cls(f) - if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float + if not isinstance(f, float): + raise TypeError("argument must be int or float.") + if _math.isinf(f) or _math.isnan(f): return cls(repr(f)) if _math.copysign(1.0, f) == 1.0: sign = 0 @@ -1906,11 +1912,12 @@ def _power_modulo(self, other, modulo, context=None): """Three argument version of __pow__""" - # if can't convert other and modulo to Decimal, raise - # TypeError; there's no point returning NotImplemented (no - # equivalent of __rpow__ for three argument pow) - other = _convert_other(other, raiseit=True) - modulo = _convert_other(modulo, raiseit=True) + other = _convert_other(other) + if other is NotImplemented: + return other + modulo = _convert_other(modulo) + if modulo is NotImplemented: + return modulo if context is None: context = getcontext() @@ -3832,11 +3839,9 @@ clamp - If 1, change exponents if too high (Default 0) """ - def __init__(self, prec=None, rounding=None, - traps=None, flags=None, - Emin=None, Emax=None, - capitals=None, clamp=None, - _ignored_flags=None): + def __init__(self, prec=None, rounding=None, Emin=None, Emax=None, + capitals=None, clamp=None, flags=None, traps=None, + _ignored_flags=None): # Set defaults; for everything except flags and _ignored_flags, # inherit from DefaultContext. try: @@ -3859,17 +3864,78 @@ if traps is None: self.traps = dc.traps.copy() elif not isinstance(traps, dict): - self.traps = dict((s, int(s in traps)) for s in _signals) + self.traps = dict((s, int(s in traps)) for s in _signals + traps) else: self.traps = traps if flags is None: self.flags = dict.fromkeys(_signals, 0) elif not isinstance(flags, dict): - self.flags = dict((s, int(s in flags)) for s in _signals) + self.flags = dict((s, int(s in flags)) for s in _signals + flags) else: self.flags = flags + def _set_integer_check(self, name, value, vmin, vmax): + if not isinstance(value, int): + raise TypeError("%s must be an integer" % name) + if vmin == '-inf': + if value > vmax: + raise ValueError("%s must be in [%s, %d]. got: %s" % (name, vmin, vmax, value)) + elif vmax == 'inf': + if value < vmin: + raise ValueError("%s must be in [%d, %s]. got: %s" % (name, vmin, vmax, value)) + else: + if value < vmin or value > vmax: + raise ValueError("%s must be in [%d, %d]. got %s" % (name, vmin, vmax, value)) + return object.__setattr__(self, name, value) + + def _set_signal_dict(self, name, d): + if not isinstance(d, dict): + raise TypeError("%s must be a signal dict" % d) + for key in d: + if not key in _signals: + raise KeyError("%s is not a valid signal dict" % d) + for key in _signals: + if not key in d: + raise KeyError("%s is not a valid signal dict" % d) + return object.__setattr__(self, name, d) + + def __setattr__(self, name, value): + if name == 'prec': + return self._set_integer_check(name, value, 1, 'inf') + elif name == 'Emin': + return self._set_integer_check(name, value, '-inf', 0) + elif name == 'Emax': + return self._set_integer_check(name, value, 0, 'inf') + elif name == 'capitals': + return self._set_integer_check(name, value, 0, 1) + elif name == 'clamp': + return self._set_integer_check(name, value, 0, 1) + elif name == 'rounding': + if not value in _rounding_modes: + # raise TypeError even for strings to have consistency + # among various implementations. + raise TypeError("%s: invalid rounding mode" % value) + return object.__setattr__(self, name, value) + elif name == 'flags' or name == 'traps': + return self._set_signal_dict(name, value) + elif name == '_ignored_flags': + return object.__setattr__(self, name, value) + else: + raise AttributeError( + "'decimal.Context' object has no attribute '%s'" % name) + + def __delattr__(self, name): + raise AttributeError("%s cannot be deleted" % name) + + # Support for pickling, copy, and deepcopy + def __reduce__(self): + flags = [sig for sig, v in self.flags.items() if v] + traps = [sig for sig, v in self.traps.items() if v] + return (self.__class__, + (self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, flags, traps)) + def __repr__(self): """Show the current context.""" s = [] @@ -3890,16 +3956,17 @@ def _shallow_copy(self): """Returns a shallow copy from self.""" - nc = Context(self.prec, self.rounding, self.traps, - self.flags, self.Emin, self.Emax, - self.capitals, self.clamp, self._ignored_flags) + nc = Context(self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, self.flags, self.traps, + self._ignored_flags) return nc def copy(self): """Returns a deep copy from self.""" - nc = Context(self.prec, self.rounding, self.traps.copy(), - self.flags.copy(), self.Emin, self.Emax, - self.capitals, self.clamp, self._ignored_flags) + nc = Context(self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, + self.flags.copy(), self.traps.copy(), + self._ignored_flags) return nc __copy__ = copy @@ -4062,6 +4129,8 @@ >>> ExtendedContext.canonical(Decimal('2.50')) Decimal('2.50') """ + if not isinstance(a, Decimal): + raise TypeError("canonical requires a Decimal as an argument.") return a.canonical(context=self) def compare(self, a, b): @@ -4372,6 +4441,8 @@ >>> ExtendedContext.is_canonical(Decimal('2.50')) True """ + if not isinstance(a, Decimal): + raise TypeError("is_canonical requires a Decimal as an argument.") return a.is_canonical() def is_finite(self, a): @@ -4964,7 +5035,7 @@ +Normal +Infinity - >>> c = Context(ExtendedContext) + >>> c = ExtendedContext.copy() >>> c.Emin = -999 >>> c.Emax = 999 >>> c.number_class(Decimal('Infinity')) @@ -6259,11 +6330,22 @@ # hash values to use for positive and negative infinities, and nans _PyHASH_INF = sys.hash_info.inf _PyHASH_NAN = sys.hash_info.nan -del sys # _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS _PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) - +del sys + +try: + import _decimal +except ImportError: + pass +else: + s1 = set(dir()) + s2 = set(dir(_decimal)) + for name in s1 - s2: + del globals()[name] + del s1, s2, name + from _decimal import * if __name__ == '__main__': import doctest, decimal