From 040e3118267bda53b34e35470f7ef0f6dc40cfad Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Sat, 15 Dec 2012 22:33:33 +0100 Subject: [PATCH] Issue #15783: Except for the number methods, the C version of decimal now supports all None default values present in decimal.py. These values were largely undocumented. --- Doc/library/decimal.rst | 95 +++++---- Lib/decimal.py | 12 +- Lib/test/test_decimal.py | 302 ++++++++++++++++++++++++++++- Misc/NEWS | 4 + Modules/_decimal/_decimal.c | 187 ++++++++++-------- Modules/_decimal/docstrings.h | 206 +++++++++++--------- Modules/_decimal/tests/deccheck.py | 34 ++++ Modules/_decimal/tests/randdec.py | 18 ++ 8 files changed, 625 insertions(+), 233 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 462d10a4347..903af6cb67d 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -459,7 +459,7 @@ Decimal objects a :class:`Decimal` instance is always canonical, so this operation returns its argument unchanged. - .. method:: compare(other[, context]) + .. method:: compare(other, context=None) Compare the values of two Decimal instances. :meth:`compare` returns a Decimal instance, and if either operand is a NaN then the result is a @@ -470,13 +470,13 @@ Decimal objects a == b ==> Decimal('0') a > b ==> Decimal('1') - .. method:: compare_signal(other[, context]) + .. method:: compare_signal(other, context=None) This operation is identical to the :meth:`compare` method, except that all NaNs signal. That is, if neither operand is a signaling NaN then any quiet NaN operand is treated as though it were a signaling NaN. - .. method:: compare_total(other) + .. method:: compare_total(other, context=None) Compare two operands using their abstract representation rather than their numerical value. Similar to the :meth:`compare` method, but the result @@ -494,13 +494,21 @@ Decimal objects higher in the total order than the second operand. See the specification for details of the total order. - .. method:: compare_total_mag(other) + This operation is unaffected by context and is quiet: no flags are changed + and no rounding is performed. As an exception, the C version may raise + InvalidOperation if the second operand cannot be converted exactly. + + .. method:: compare_total_mag(other, context=None) Compare two operands using their abstract representation rather than their value as in :meth:`compare_total`, but ignoring the sign of each operand. ``x.compare_total_mag(y)`` is equivalent to ``x.copy_abs().compare_total(y.copy_abs())``. + This operation is unaffected by context and is quiet: no flags are changed + and no rounding is performed. As an exception, the C version may raise + InvalidOperation if the second operand cannot be converted exactly. + .. method:: conjugate() Just returns self, this method is only to comply with the Decimal @@ -517,7 +525,7 @@ Decimal objects Return the negation of the argument. This operation is unaffected by the context and is quiet: no flags are changed and no rounding is performed. - .. method:: copy_sign(other) + .. method:: copy_sign(other, context=None) Return a copy of the first operand with the sign set to be the same as the sign of the second operand. For example: @@ -525,10 +533,11 @@ Decimal objects >>> Decimal('2.3').copy_sign(Decimal('-1.5')) Decimal('-2.3') - This operation is unaffected by the context and is quiet: no flags are - changed and no rounding is performed. + This operation is unaffected by context and is quiet: no flags are changed + and no rounding is performed. As an exception, the C version may raise + InvalidOperation if the second operand cannot be converted exactly. - .. method:: exp([context]) + .. method:: exp(context=None) Return the value of the (natural) exponential function ``e**x`` at the given number. The result is correctly rounded using the @@ -565,7 +574,7 @@ Decimal objects .. versionadded:: 3.1 - .. method:: fma(other, third[, context]) + .. method:: fma(other, third, context=None) Fused multiply-add. Return self*other+third with no rounding of the intermediate product self*other. @@ -594,7 +603,7 @@ Decimal objects Return :const:`True` if the argument is a (quiet or signaling) NaN and :const:`False` otherwise. - .. method:: is_normal() + .. method:: is_normal(context=None) Return :const:`True` if the argument is a *normal* finite number. Return :const:`False` if the argument is zero, subnormal, infinite or a NaN. @@ -614,7 +623,7 @@ Decimal objects Return :const:`True` if the argument is a signaling NaN and :const:`False` otherwise. - .. method:: is_subnormal() + .. method:: is_subnormal(context=None) Return :const:`True` if the argument is subnormal, and :const:`False` otherwise. @@ -624,17 +633,17 @@ Decimal objects Return :const:`True` if the argument is a (positive or negative) zero and :const:`False` otherwise. - .. method:: ln([context]) + .. method:: ln(context=None) Return the natural (base e) logarithm of the operand. The result is correctly rounded using the :const:`ROUND_HALF_EVEN` rounding mode. - .. method:: log10([context]) + .. method:: log10(context=None) Return the base ten logarithm of the operand. The result is correctly rounded using the :const:`ROUND_HALF_EVEN` rounding mode. - .. method:: logb([context]) + .. method:: logb(context=None) For a nonzero number, return the adjusted exponent of its operand as a :class:`Decimal` instance. If the operand is a zero then @@ -642,73 +651,73 @@ Decimal objects is raised. If the operand is an infinity then ``Decimal('Infinity')`` is returned. - .. method:: logical_and(other[, context]) + .. method:: logical_and(other, context=None) :meth:`logical_and` is a logical operation which takes two *logical operands* (see :ref:`logical_operands_label`). The result is the digit-wise ``and`` of the two operands. - .. method:: logical_invert([context]) + .. method:: logical_invert(context=None) :meth:`logical_invert` is a logical operation. The result is the digit-wise inversion of the operand. - .. method:: logical_or(other[, context]) + .. method:: logical_or(other, context=None) :meth:`logical_or` is a logical operation which takes two *logical operands* (see :ref:`logical_operands_label`). The result is the digit-wise ``or`` of the two operands. - .. method:: logical_xor(other[, context]) + .. method:: logical_xor(other, context=None) :meth:`logical_xor` is a logical operation which takes two *logical operands* (see :ref:`logical_operands_label`). The result is the digit-wise exclusive or of the two operands. - .. method:: max(other[, context]) + .. method:: max(other, context=None) Like ``max(self, other)`` except that the context rounding rule is applied before returning and that :const:`NaN` values are either signaled or ignored (depending on the context and whether they are signaling or quiet). - .. method:: max_mag(other[, context]) + .. method:: max_mag(other, context=None) Similar to the :meth:`.max` method, but the comparison is done using the absolute values of the operands. - .. method:: min(other[, context]) + .. method:: min(other, context=None) Like ``min(self, other)`` except that the context rounding rule is applied before returning and that :const:`NaN` values are either signaled or ignored (depending on the context and whether they are signaling or quiet). - .. method:: min_mag(other[, context]) + .. method:: min_mag(other, context=None) Similar to the :meth:`.min` method, but the comparison is done using the absolute values of the operands. - .. method:: next_minus([context]) + .. method:: next_minus(context=None) Return the largest number representable in the given context (or in the current thread's context if no context is given) that is smaller than the given operand. - .. method:: next_plus([context]) + .. method:: next_plus(context=None) Return the smallest number representable in the given context (or in the current thread's context if no context is given) that is larger than the given operand. - .. method:: next_toward(other[, context]) + .. method:: next_toward(other, context=None) If the two operands are unequal, return the number closest to the first operand in the direction of the second operand. If both operands are numerically equal, return a copy of the first operand with the sign set to be the same as the sign of the second operand. - .. method:: normalize([context]) + .. method:: normalize(context=None) Normalize the number by stripping the rightmost trailing zeros and converting any result equal to :const:`Decimal('0')` to @@ -717,7 +726,7 @@ Decimal objects ``Decimal('0.321000e+2')`` both normalize to the equivalent value ``Decimal('32.1')``. - .. method:: number_class([context]) + .. method:: number_class(context=None) Return a string describing the *class* of the operand. The returned value is one of the following ten strings. @@ -733,7 +742,7 @@ Decimal objects * ``"NaN"``, indicating that the operand is a quiet NaN (Not a Number). * ``"sNaN"``, indicating that the operand is a signaling NaN. - .. method:: quantize(exp[, rounding[, context[, watchexp]]]) + .. method:: quantize(exp, rounding=None, context=None, watchexp=True) Return a value equal to the first operand after rounding and having the exponent of the second operand. @@ -771,7 +780,7 @@ Decimal objects class does all its arithmetic. Included for compatibility with the specification. - .. method:: remainder_near(other[, context]) + .. method:: remainder_near(other, context=None) Return the remainder from dividing *self* by *other*. This differs from ``self % other`` in that the sign of the remainder is chosen so as to @@ -789,7 +798,7 @@ Decimal objects >>> Decimal(35).remainder_near(Decimal(10)) Decimal('-5') - .. method:: rotate(other[, context]) + .. method:: rotate(other, context=None) Return the result of rotating the digits of the first operand by an amount specified by the second operand. The second operand must be an integer in @@ -800,18 +809,22 @@ Decimal objects length precision if necessary. The sign and exponent of the first operand are unchanged. - .. method:: same_quantum(other[, context]) + .. method:: same_quantum(other, context=None) Test whether self and other have the same exponent or whether both are :const:`NaN`. - .. method:: scaleb(other[, context]) + This operation is unaffected by context and is quiet: no flags are changed + and no rounding is performed. As an exception, the C version may raise + InvalidOperation if the second operand cannot be converted exactly. + + .. method:: scaleb(other, context=None) Return the first operand with exponent adjusted by the second. Equivalently, return the first operand multiplied by ``10**other``. The second operand must be an integer. - .. method:: shift(other[, context]) + .. method:: shift(other, context=None) Return the result of shifting the digits of the first operand by an amount specified by the second operand. The second operand must be an integer in @@ -821,12 +834,12 @@ Decimal objects right. Digits shifted into the coefficient are zeros. The sign and exponent of the first operand are unchanged. - .. method:: sqrt([context]) + .. method:: sqrt(context=None) Return the square root of the argument to full precision. - .. method:: to_eng_string([context]) + .. method:: to_eng_string(context=None) Convert to an engineering-type string. @@ -834,12 +847,12 @@ Decimal objects are up to 3 digits left of the decimal place. For example, converts ``Decimal('123E+1')`` to ``Decimal('1.23E+3')`` - .. method:: to_integral([rounding[, context]]) + .. method:: to_integral(rounding=None, context=None) Identical to the :meth:`to_integral_value` method. The ``to_integral`` name has been kept for compatibility with older versions. - .. method:: to_integral_exact([rounding[, context]]) + .. method:: to_integral_exact(rounding=None, context=None) Round to the nearest integer, signaling :const:`Inexact` or :const:`Rounded` as appropriate if rounding occurs. The rounding mode is @@ -847,7 +860,7 @@ Decimal objects ``context``. If neither parameter is given then the rounding mode of the current context is used. - .. method:: to_integral_value([rounding[, context]]) + .. method:: to_integral_value(rounding=None, context=None) Round to the nearest integer without signaling :const:`Inexact` or :const:`Rounded`. If given, applies *rounding*; otherwise, uses the @@ -893,10 +906,10 @@ Each thread has its own current context which is accessed or changed using the You can also use the :keyword:`with` statement and the :func:`localcontext` function to temporarily change the active context. -.. function:: localcontext([c]) +.. function:: localcontext(ctx=None) Return a context manager that will set the current context for the active thread - to a copy of *c* on entry to the with-statement and restore the previous context + to a copy of *ctx* 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. @@ -1315,7 +1328,7 @@ In addition to the three supplied contexts, new contexts can be created with the identity operation. - .. method:: power(x, y[, modulo]) + .. method:: power(x, y, modulo=None) Return ``x`` to the power of ``y``, reduced modulo ``modulo`` if given. diff --git a/Lib/decimal.py b/Lib/decimal.py index b74ab0134d9..746b34a8946 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -2596,7 +2596,7 @@ class Decimal(object): ans = ans._fix(context) return ans - def same_quantum(self, other): + def same_quantum(self, other, context=None): """Return True if self and other have the same exponent; otherwise return False. @@ -2914,7 +2914,7 @@ class Decimal(object): except TypeError: return 0 - def canonical(self, context=None): + def canonical(self): """Returns the same Decimal object. As we do not have different encodings for the same number, the @@ -2934,7 +2934,7 @@ class Decimal(object): return ans return self.compare(other, context=context) - def compare_total(self, other): + def compare_total(self, other, context=None): """Compares self to other using the abstract representations. This is not like the standard compare, which use their numerical @@ -3007,7 +3007,7 @@ class Decimal(object): return _Zero - def compare_total_mag(self, other): + def compare_total_mag(self, other, context=None): """Compares self to other using abstract repr., ignoring sign. Like compare_total, but with operand's sign ignored and assumed to be 0. @@ -3029,7 +3029,7 @@ class Decimal(object): else: return _dec_from_triple(1, self._int, self._exp, self._is_special) - def copy_sign(self, other): + def copy_sign(self, other, context=None): """Returns self with the sign of other.""" other = _convert_other(other, raiseit=True) return _dec_from_triple(other._sign, self._int, @@ -4182,7 +4182,7 @@ class Context(object): """ if not isinstance(a, Decimal): raise TypeError("canonical requires a Decimal as an argument.") - return a.canonical(context=self) + return a.canonical() def compare(self, a, b): """Compares values numerically. diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 5195930834d..0dddc4ba6bb 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2089,6 +2089,248 @@ class UsabilityTest(unittest.TestCase): self.assertEqual(str(Decimal(0).sqrt()), str(c.sqrt(Decimal(0)))) + def test_none_args(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + DivisionByZero = self.decimal.DivisionByZero + Overflow = self.decimal.Overflow + Underflow = self.decimal.Underflow + Subnormal = self.decimal.Subnormal + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + Clamped = self.decimal.Clamped + ROUND_HALF_EVEN = self.decimal.ROUND_HALF_EVEN + ROUND_DOWN = self.decimal.ROUND_DOWN + ROUND_UP = self.decimal.ROUND_UP + + with localcontext(Context()) as c: + c.prec = 7 + c.Emax = 999 + c.Emin = -999 + + x = Decimal("111") + y = Decimal("1e9999") + z = Decimal("1e-9999") + + ##### Unary functions + c.clear_flags() + self.assertEqual(str(x.exp(context=None)), '1.609487E+48') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(Overflow, y.exp, context=None) + self.assertTrue(c.flags[Overflow]) + + self.assertIs(z.is_normal(context=None), False) + self.assertIs(z.is_subnormal(context=None), True) + + c.clear_flags() + self.assertEqual(str(x.ln(context=None)), '4.709530') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal(-1).ln, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(x.log10(context=None)), '2.045323') + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal(-1).log10, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(x.logb(context=None)), '2') + self.assertRaises(DivisionByZero, Decimal(0).logb, context=None) + self.assertTrue(c.flags[DivisionByZero]) + + c.clear_flags() + self.assertEqual(str(x.logical_invert(context=None)), '1111000') + self.assertRaises(InvalidOperation, y.logical_invert, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(y.next_minus(context=None)), '9.999999E+999') + self.assertRaises(InvalidOperation, Decimal('sNaN').next_minus, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(y.next_plus(context=None)), 'Infinity') + self.assertRaises(InvalidOperation, Decimal('sNaN').next_plus, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + self.assertEqual(str(z.normalize(context=None)), '0') + self.assertRaises(Overflow, y.normalize, context=None) + self.assertTrue(c.flags[Overflow]) + + self.assertEqual(str(z.number_class(context=None)), '+Subnormal') + + c.clear_flags() + self.assertEqual(str(z.sqrt(context=None)), '0E-1005') + self.assertTrue(c.flags[Clamped]) + self.assertTrue(c.flags[Inexact]) + self.assertTrue(c.flags[Rounded]) + self.assertTrue(c.flags[Subnormal]) + self.assertTrue(c.flags[Underflow]) + c.clear_flags() + self.assertRaises(Overflow, y.sqrt, context=None) + self.assertTrue(c.flags[Overflow]) + + c.capitals = 0 + self.assertEqual(str(z.to_eng_string(context=None)), '1e-9999') + c.capitals = 1 + + + ##### Binary functions + c.clear_flags() + ans = str(x.compare(Decimal('Nan891287828'), context=None)) + self.assertEqual(ans, 'NaN1287828') + self.assertRaises(InvalidOperation, x.compare, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.compare_signal(8224, context=None)) + self.assertEqual(ans, '-1') + self.assertRaises(InvalidOperation, x.compare_signal, Decimal('NaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_and(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.logical_and, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_or(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.logical_or, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.logical_xor(101, context=None)) + self.assertEqual(ans, '10') + self.assertRaises(InvalidOperation, x.logical_xor, 123, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.max(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.max, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.max_mag(101, context=None)) + self.assertEqual(ans, '111') + self.assertRaises(InvalidOperation, x.max_mag, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.min(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.min, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.min_mag(101, context=None)) + self.assertEqual(ans, '101') + self.assertRaises(InvalidOperation, x.min_mag, Decimal('sNaN'), context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.remainder_near(101, context=None)) + self.assertEqual(ans, '10') + self.assertRaises(InvalidOperation, y.remainder_near, 101, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.rotate(2, context=None)) + self.assertEqual(ans, '11100') + self.assertRaises(InvalidOperation, x.rotate, 101, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.scaleb(7, context=None)) + self.assertEqual(ans, '1.11E+9') + self.assertRaises(InvalidOperation, x.scaleb, 10000, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + ans = str(x.shift(2, context=None)) + self.assertEqual(ans, '11100') + self.assertRaises(InvalidOperation, x.shift, 10000, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + + ##### Ternary functions + c.clear_flags() + ans = str(x.fma(2, 3, context=None)) + self.assertEqual(ans, '225') + self.assertRaises(Overflow, x.fma, Decimal('1e9999'), 3, context=None) + self.assertTrue(c.flags[Overflow]) + + + ##### Special cases + c.rounding = ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = ROUND_DOWN + ans = str(Decimal('1.5').to_integral(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral(rounding=ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = ROUND_DOWN + ans = str(Decimal('1.5').to_integral_value(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral_value(rounding=ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_value, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = ROUND_HALF_EVEN + ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) + self.assertEqual(ans, '2') + c.rounding = ROUND_DOWN + ans = str(Decimal('1.5').to_integral_exact(rounding=None, context=None)) + self.assertEqual(ans, '1') + ans = str(Decimal('1.5').to_integral_exact(rounding=ROUND_UP, context=None)) + self.assertEqual(ans, '2') + c.clear_flags() + self.assertRaises(InvalidOperation, Decimal('sNaN').to_integral_exact, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + c.rounding = ROUND_UP + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) + self.assertEqual(ans, '1.501') + c.rounding = ROUND_DOWN + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=None, context=None)) + self.assertEqual(ans, '1.500') + ans = str(Decimal('1.50001').quantize(exp=Decimal('1e-3'), rounding=ROUND_UP, context=None)) + self.assertEqual(ans, '1.501') + c.clear_flags() + self.assertRaises(InvalidOperation, y.quantize, Decimal('1e-10'), rounding=ROUND_UP, context=None) + self.assertTrue(c.flags[InvalidOperation]) + + with localcontext(Context()) as context: + context.prec = 7 + context.Emax = 999 + context.Emin = -999 + with localcontext(ctx=None) as c: + self.assertEqual(c.prec, 7) + self.assertEqual(c.Emax, 999) + self.assertEqual(c.Emin, -999) + def test_conversions_from_int(self): # Check that methods taking a second Decimal argument will # always accept an integer in place of a Decimal. @@ -2423,14 +2665,11 @@ class PythonAPItests(unittest.TestCase): self.assertRaises(TypeError, D.from_float, 1.1, context=xc) self.assertRaises(TypeError, D(0).as_tuple, context=xc) - if (self.decimal == C): - self.assertRaises(TypeError, D(1).canonical, context=xc) - self.assertEqual(D("-1").copy_abs(context=xc), 1) - self.assertEqual(D("1").copy_negate(context=xc), -1) - else: - self.assertEqual(D(1).canonical(context=xc), 1) - self.assertRaises(TypeError, D("-1").copy_abs, context=xc) - self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + self.assertEqual(D(1).canonical(), 1) + self.assertRaises(TypeError, D("-1").copy_abs, context=xc) + self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + self.assertRaises(TypeError, D(1).canonical, context="x") + self.assertRaises(TypeError, D(1).canonical, xyz="x") def test_exception_hierarchy(self): @@ -4593,6 +4832,9 @@ class CWhitebox(unittest.TestCase): # Invalid local context self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass', locals()) + self.assertRaises(TypeError, exec, + 'with localcontext(context=getcontext()): pass', + locals()) # setcontext saved_context = getcontext() @@ -4826,6 +5068,50 @@ class CWhitebox(unittest.TestCase): c.prec = 2 self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501) + def test_va_args_exceptions(self): + Decimal = C.Decimal + Context = C.Context + + x = Decimal("10001111111") + + for attr in ['exp', 'is_normal', 'is_subnormal', 'ln', 'log10', + 'logb', 'logical_invert', 'next_minus', 'next_plus', + 'normalize', 'number_class', 'sqrt', 'to_eng_string']: + func = getattr(x, attr) + self.assertRaises(TypeError, func, context="x") + self.assertRaises(TypeError, func, "x", context=None) + + for attr in ['compare', 'compare_signal', 'logical_and', + 'logical_or', 'max', 'max_mag', 'min', 'min_mag', + 'remainder_near', 'rotate', 'scaleb', 'shift']: + func = getattr(x, attr) + self.assertRaises(TypeError, func, context="x") + self.assertRaises(TypeError, func, "x", context=None) + + self.assertRaises(TypeError, x.to_integral, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral, [], []) + + self.assertRaises(TypeError, x.to_integral_value, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral_value, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral_value, [], []) + + self.assertRaises(TypeError, x.to_integral_exact, rounding=None, context=[]) + self.assertRaises(TypeError, x.to_integral_exact, rounding={}, context=[]) + self.assertRaises(TypeError, x.to_integral_exact, [], []) + + self.assertRaises(TypeError, x.fma, 1, 2, context="x") + self.assertRaises(TypeError, x.fma, 1, 2, "x", context=None) + + self.assertRaises(TypeError, x.quantize, 1, [], context=None) + self.assertRaises(TypeError, x.quantize, 1, [], rounding=None) + self.assertRaises(TypeError, x.quantize, 1, [], []) + + c = Context() + self.assertRaises(TypeError, c.power, 1, 2, mod="x") + self.assertRaises(TypeError, c.power, 1, "x", mod=None) + self.assertRaises(TypeError, c.power, "x", 2, mod=None) + @requires_extra_functionality def test_c_context_templates(self): self.assertEqual( diff --git a/Misc/NEWS b/Misc/NEWS index 5020c9d42d1..e396da9e8f6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -108,6 +108,10 @@ Core and Builtins Library ------- +- Issue #15783: Except for the number methods, the C version of decimal now + supports all None default values present in decimal.py. These values were + largely undocumented. + - Issue #16298: In HTTPResponse.read(), close the socket when there is no Content-Length and the incoming stream is finished. Patch by Eran Rundstein. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 0610a8bcec0..3490cd2a451 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1486,7 +1486,10 @@ static PyGetSetDef context_getsets [] = } #define CONTEXT_CHECK_VA(obj) \ - if (!PyDecContext_Check(obj)) { \ + if (obj == Py_None) { \ + CURRENT_CONTEXT(obj); \ + } \ + else if (!PyDecContext_Check(obj)) { \ PyErr_SetString(PyExc_TypeError, \ "optional argument must be a context"); \ return NULL; \ @@ -1715,18 +1718,25 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) * owns one reference to the global (outer) context and one * to the local (inner) context. */ static PyObject * -ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args) +ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"ctx", NULL}; PyDecContextManagerObject *self; - PyObject *local; + PyObject *local = Py_None; PyObject *global; CURRENT_CONTEXT(global); - local = global; - if (!PyArg_ParseTuple(args, "|O", &local)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &local)) { + return NULL; + } + if (local == Py_None) { + local = global; + } + else if (!PyDecContext_Check(local)) { + PyErr_SetString(PyExc_TypeError, + "optional argument must be a context"); return NULL; } - CONTEXT_CHECK_VA(local); self = PyObject_New(PyDecContextManagerObject, &PyDecContextManager_Type); @@ -2749,9 +2759,8 @@ dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"value", "context", NULL}; PyObject *v = NULL; - PyObject *context; + PyObject *context = Py_None; - CURRENT_CONTEXT(context); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &v, &context)) { return NULL; @@ -3315,20 +3324,23 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"rounding", "context", NULL}; PyObject *result; - PyObject *context; + PyObject *rounding = Py_None; + PyObject *context = Py_None; uint32_t status = 0; mpd_context_t workctx; - int round = -1; - CURRENT_CONTEXT(context); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, - &round, &context)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, + &rounding, &context)) { return NULL; } CONTEXT_CHECK_VA(context); workctx = *CTX(context); - if (round >= 0) { + if (rounding != Py_None) { + int round = getround(rounding); + if (round < 0) { + return NULL; + } if (!mpd_qsetround(&workctx, round)) { return type_error_ptr(invalid_rounding_err); } @@ -3353,20 +3365,23 @@ PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"rounding", "context", NULL}; PyObject *result; - PyObject *context; + PyObject *rounding = Py_None; + PyObject *context = Py_None; uint32_t status = 0; mpd_context_t workctx; - int round = -1; - CURRENT_CONTEXT(context); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, - &round, &context)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, + &rounding, &context)) { return NULL; } CONTEXT_CHECK_VA(context); workctx = *CTX(context); - if (round >= 0) { + if (rounding != Py_None) { + int round = getround(rounding); + if (round < 0) { + return NULL; + } if (!mpd_qsetround(&workctx, round)) { return type_error_ptr(invalid_rounding_err); } @@ -3633,9 +3648,8 @@ static PyObject * \ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ static char *kwlist[] = {"context", NULL}; \ - PyObject *context; \ + PyObject *context = Py_None; \ \ - CURRENT_CONTEXT(context); \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ &context)) { \ return NULL; \ @@ -3652,10 +3666,9 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ static char *kwlist[] = {"context", NULL}; \ PyObject *result; \ - PyObject *context; \ + PyObject *context = Py_None; \ uint32_t status = 0; \ \ - CURRENT_CONTEXT(context); \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ &context)) { \ return NULL; \ @@ -3675,49 +3688,18 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ return result; \ } -/* Unary function with an optional context arg. The actual MPDFUNC - only takes a status parameter. */ -#define Dec_UnaryFuncVA_NO_CTX(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ -{ \ - static char *kwlist[] = {"context", NULL}; \ - PyObject *result; \ - PyObject *context; \ - uint32_t status = 0; \ - \ - CURRENT_CONTEXT(context); \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ - &context)) { \ - return NULL; \ - } \ - CONTEXT_CHECK_VA(context); \ - \ - if ((result = dec_alloc()) == NULL) { \ - return NULL; \ - } \ - \ - MPDFUNC(MPD(result), MPD(self), &status); \ - if (dec_addstatus(context, status)) { \ - Py_DECREF(result); \ - return NULL; \ - } \ - \ - return result; \ -} - /* Binary function with an optional context arg. */ #define Dec_BinaryFuncVA(MPDFUNC) \ static PyObject * \ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ static char *kwlist[] = {"other", "context", NULL}; \ - PyObject *other, *context; \ + PyObject *other; \ PyObject *a, *b; \ PyObject *result; \ + PyObject *context = Py_None; \ uint32_t status = 0; \ \ - CURRENT_CONTEXT(context); \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ &other, &context)) { \ return NULL; \ @@ -3750,11 +3732,11 @@ static PyObject * \ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ static char *kwlist[] = {"other", "context", NULL}; \ - PyObject *other, *context; \ + PyObject *context = Py_None; \ + PyObject *other; \ PyObject *a, *b; \ PyObject *result; \ \ - CURRENT_CONTEXT(context); \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ &other, &context)) { \ return NULL; \ @@ -3781,12 +3763,12 @@ static PyObject * \ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ static char *kwlist[] = {"other", "third", "context", NULL}; \ - PyObject *other, *third, *context; \ + PyObject *other, *third; \ PyObject *a, *b, *c; \ PyObject *result; \ + PyObject *context = Py_None; \ uint32_t status = 0; \ \ - CURRENT_CONTEXT(context); \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, \ &other, &third, &context)) { \ return NULL; \ @@ -4019,9 +4001,45 @@ dec_mpd_radix(PyObject *self UNUSED, PyObject *dummy UNUSED) return result; } -/* Unary functions, optional context arg for conversion errors */ -Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_abs) -Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_negate) +static PyObject * +dec_mpd_qcopy_abs(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result; + uint32_t status = 0; + + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + mpd_qcopy_abs(MPD(result), MPD(self), &status); + if (status & MPD_Malloc_error) { + Py_DECREF(result); + PyErr_NoMemory(); + return NULL; + } + + return result; +} + +static PyObject * +dec_mpd_qcopy_negate(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result; + uint32_t status = 0; + + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + mpd_qcopy_negate(MPD(result), MPD(self), &status); + if (status & MPD_Malloc_error) { + Py_DECREF(result); + PyErr_NoMemory(); + return NULL; + } + + return result; +} /* Unary functions, optional context arg */ Dec_UnaryFuncVA(mpd_qinvert) @@ -4031,10 +4049,9 @@ static PyObject * dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"context", NULL}; - PyObject *context; + PyObject *context = Py_None; const char *cp; - CURRENT_CONTEXT(context); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &context)) { return NULL; @@ -4050,11 +4067,10 @@ dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"context", NULL}; PyObject *result; - PyObject *context; + PyObject *context = Py_None; mpd_ssize_t size; char *s; - CURRENT_CONTEXT(context); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &context)) { return NULL; @@ -4081,12 +4097,12 @@ static PyObject * dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"other", "context", NULL}; - PyObject *other, *context; + PyObject *other; PyObject *a, *b; PyObject *result; + PyObject *context = Py_None; uint32_t status = 0; - CURRENT_CONTEXT(context); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &other, &context)) { return NULL; @@ -4116,11 +4132,11 @@ static PyObject * dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"other", "context", NULL}; - PyObject *other, *context; + PyObject *other; PyObject *a, *b; PyObject *result; + PyObject *context = Py_None; - CURRENT_CONTEXT(context); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &other, &context)) { return NULL; @@ -4148,22 +4164,25 @@ static PyObject * dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"exp", "rounding", "context", NULL}; - PyObject *w, *context; - PyObject *a, *b; + PyObject *rounding = Py_None; + PyObject *context = Py_None; + PyObject *w, *a, *b; PyObject *result; uint32_t status = 0; mpd_context_t workctx; - int round = -1; - CURRENT_CONTEXT(context); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", kwlist, - &w, &round, &context)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, + &w, &rounding, &context)) { return NULL; } CONTEXT_CHECK_VA(context); workctx = *CTX(context); - if (round >= 0) { + if (rounding != Py_None) { + int round = getround(rounding); + if (round < 0) { + return NULL; + } if (!mpd_qsetround(&workctx, round)) { return type_error_ptr(invalid_rounding_err); } @@ -4585,8 +4604,8 @@ static PyMethodDef dec_methods [] = { "radix", dec_mpd_radix, METH_NOARGS, doc_radix }, /* Unary functions, optional context arg for conversion errors */ - { "copy_abs", (PyCFunction)dec_mpd_qcopy_abs, METH_VARARGS|METH_KEYWORDS, doc_copy_abs }, - { "copy_negate", (PyCFunction)dec_mpd_qcopy_negate, METH_VARARGS|METH_KEYWORDS, doc_copy_negate }, + { "copy_abs", dec_mpd_qcopy_abs, METH_NOARGS, doc_copy_abs }, + { "copy_negate", dec_mpd_qcopy_negate, METH_NOARGS, doc_copy_negate }, /* Unary functions, optional context arg */ { "logb", (PyCFunction)dec_mpd_qlogb, METH_VARARGS|METH_KEYWORDS, doc_logb }, @@ -4916,7 +4935,7 @@ static PyObject * ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"a", "b", "modulo", NULL}; - PyObject *base, *exp, *mod = NULL; + PyObject *base, *exp, *mod = Py_None; PyObject *a, *b, *c = NULL; PyObject *result; uint32_t status = 0; @@ -4928,7 +4947,7 @@ ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) CONVERT_BINOP_RAISE(&a, &b, base, exp, context); - if (mod != NULL) { + if (mod != Py_None) { if (!convert_op(TYPE_ERR, &c, mod, context)) { Py_DECREF(a); Py_DECREF(b); @@ -5361,7 +5380,7 @@ static PyMethodDef _decimal_methods [] = { { "getcontext", (PyCFunction)PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext}, { "setcontext", (PyCFunction)PyDec_SetCurrentContext, METH_O, doc_setcontext}, - { "localcontext", (PyCFunction)ctxmanager_new, METH_VARARGS, doc_localcontext}, + { "localcontext", (PyCFunction)ctxmanager_new, METH_VARARGS|METH_KEYWORDS, doc_localcontext}, #ifdef EXTRA_FUNCTIONALITY { "IEEEContext", (PyCFunction)ieee_context, METH_O, doc_ieee_context}, #endif diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index cf150c7eac4..a6490b982a3 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -28,10 +28,10 @@ setcontext(c) - Set a new default context.\n\ \n"); PyDoc_STRVAR(doc_localcontext,"\n\ -localcontext(c) - Return a context manager that will set the default context\n\ -to a copy of c on entry to the with-statement and restore the previous default\n\ -context when exiting the with-statement. If no context is specified, a copy of\n\ -the current default context is used.\n\ +localcontext(ctx=None) - Return a context manager that will set the default\n\ +context to a copy of ctx on entry to the with-statement and restore the\n\ +previous default context when exiting the with-statement. If no context is\n\ +specified, a copy of the current default context is used.\n\ \n"); #ifdef EXTRA_FUNCTIONALITY @@ -49,8 +49,7 @@ DECIMAL32, DECIMAL64 and DECIMAL128 are provided.\n\ /******************************************************************************/ PyDoc_STRVAR(doc_decimal,"\n\ -Decimal([value[, context]]): Construct a new Decimal object from value.\n\ -\n\ +Decimal(value=\"0\", context=None): Construct a new Decimal object.\n\ value can be an integer, string, tuple, or another Decimal object.\n\ If no value is given, return Decimal('0'). The context does not affect\n\ the conversion and is only passed to determine if the InvalidOperation\n\ @@ -74,7 +73,7 @@ returns its argument unchanged.\n\ \n"); PyDoc_STRVAR(doc_compare,"\n\ -compare(other[, context]) - Compare self to other. Return a decimal value:\n\ +compare(other, context=None) - Compare self to other. Return a decimal value:\n\ \n\ a or b is a NaN ==> Decimal('NaN')\n\ a < b ==> Decimal('-1')\n\ @@ -83,16 +82,16 @@ compare(other[, context]) - Compare self to other. Return a decimal value:\n\ \n"); PyDoc_STRVAR(doc_compare_signal,"\n\ -compare_signal(other[, context]) - Identical to compare, except that\n\ +compare_signal(other, context=None) - Identical to compare, except that\n\ all NaNs signal.\n\ \n"); PyDoc_STRVAR(doc_compare_total,"\n\ -compare_total(other) - Compare two operands using their abstract representation\n\ -rather than their numerical value. Similar to the compare() method, but the\n\ -result gives a total ordering on Decimal instances. Two Decimal instances with\n\ -the same numeric value but different representations compare unequal in this\n\ -ordering:\n\ +compare_total(other, context=None) - Compare two operands using their\n\ +abstract representation rather than their numerical value. Similar to the\n\ +compare() method, but the result gives a total ordering on Decimal instances.\n\ +Two Decimal instances with the same numeric value but different representations\n\ +compare unequal in this ordering:\n\ \n\ >>> Decimal('12.0').compare_total(Decimal('12'))\n\ Decimal('-1')\n\ @@ -102,13 +101,21 @@ of this function is Decimal('0') if both operands have the same representation,\ Decimal('-1') if the first operand is lower in the total order than the second,\n\ and Decimal('1') if the first operand is higher in the total order than the\n\ second operand. See the specification for details of the total order.\n\ +\n\ +This operation is unaffected by context and is quiet: no flags are changed\n\ +and no rounding is performed. As an exception, the C version may raise\n\ +InvalidOperation if the second operand cannot be converted exactly.\n\ \n"); PyDoc_STRVAR(doc_compare_total_mag,"\n\ -compare_total_mag(other) - Compare two operands using their abstract\n\ -representation rather than their value as in compare_total(), but\n\ -ignoring the sign of each operand. x.compare_total_mag(y) is\n\ -equivalent to x.copy_abs().compare_total(y.copy_abs()).\n\ +compare_total_mag(other, context=None) - Compare two operands using their\n\ +abstract representation rather than their value as in compare_total(), but\n\ +ignoring the sign of each operand. x.compare_total_mag(y) is equivalent to\n\ +x.copy_abs().compare_total(y.copy_abs()).\n\ +\n\ +This operation is unaffected by context and is quiet: no flags are changed\n\ +and no rounding is performed. As an exception, the C version may raise\n\ +InvalidOperation if the second operand cannot be converted exactly.\n\ \n"); PyDoc_STRVAR(doc_conjugate,"\n\ @@ -117,31 +124,32 @@ conjugate() - Return self.\n\ PyDoc_STRVAR(doc_copy_abs,"\n\ copy_abs() - Return the absolute value of the argument. This operation\n\ -is unaffected by the context and is quiet: no flags are changed and no\n\ -rounding is performed.\n\ +is unaffected by context and is quiet: no flags are changed and no rounding\n\ +is performed.\n\ \n"); PyDoc_STRVAR(doc_copy_negate,"\n\ copy_negate() - Return the negation of the argument. This operation is\n\ -unaffected by the context and is quiet: no flags are changed and no\n\ -rounding is performed.\n\ +unaffected by context and is quiet: no flags are changed and no rounding\n\ +is performed.\n\ \n"); PyDoc_STRVAR(doc_copy_sign,"\n\ -copy_sign(other) - Return a copy of the first operand with the sign set\n\ -to be the same as the sign of the second operand. For example:\n\ +copy_sign(other, context=None) - Return a copy of the first operand with\n\ +the sign set to be the same as the sign of the second operand. For example:\n\ \n\ >>> Decimal('2.3').copy_sign(Decimal('-1.5'))\n\ Decimal('-2.3')\n\ \n\ -This operation is unaffected by the context and is quiet: no flags are\n\ -changed and no rounding is performed.\n\ +This operation is unaffected by context and is quiet: no flags are changed\n\ +and no rounding is performed. As an exception, the C version may raise\n\ +InvalidOperation if the second operand cannot be converted exactly.\n\ \n"); PyDoc_STRVAR(doc_exp,"\n\ -exp([context]) - Return the value of the (natural) exponential function e**x\n\ -at the given number. The function always uses the ROUND_HALF_EVEN mode and\n\ -the result is correctly rounded.\n\ +exp(context=None) - Return the value of the (natural) exponential function\n\ +e**x at the given number. The function always uses the ROUND_HALF_EVEN mode\n\ +and the result is correctly rounded.\n\ \n"); PyDoc_STRVAR(doc_from_float,"\n\ @@ -161,7 +169,7 @@ Decimal.from_float(0.1) is not the same as Decimal('0.1').\n\ \n"); PyDoc_STRVAR(doc_fma,"\n\ -fma(other, third[, context]) - Fused multiply-add. Return self*other+third\n\ +fma(other, third, context=None) - Fused multiply-add. Return self*other+third\n\ with no rounding of the intermediate product self*other.\n\ \n\ >>> Decimal(2).fma(3, 5)\n\ @@ -191,9 +199,9 @@ False otherwise.\n\ \n"); PyDoc_STRVAR(doc_is_normal,"\n\ -is_normal([context]) - Return True if the argument is a normal finite non-zero\n\ -number with an adjusted exponent greater than or equal to Emin. Return False\n\ -if the argument is zero, subnormal, infinite or a NaN.\n\ +is_normal(context=None) - Return True if the argument is a normal finite\n\ +non-zero number with an adjusted exponent greater than or equal to Emin.\n\ +Return False if the argument is zero, subnormal, infinite or a NaN.\n\ \n"); PyDoc_STRVAR(doc_is_qnan,"\n\ @@ -210,8 +218,8 @@ is_snan() - Return True if the argument is a signaling NaN and False otherwise.\ \n"); PyDoc_STRVAR(doc_is_subnormal,"\n\ -is_subnormal([context]) - Return True if the argument is subnormal, and False\n\ -otherwise. A number is subnormal if it is non-zero, finite, and has an\n\ +is_subnormal(context=None) - Return True if the argument is subnormal, and\n\ +False otherwise. A number is subnormal if it is non-zero, finite, and has an\n\ adjusted exponent less than Emin.\n\ \n"); @@ -221,94 +229,94 @@ False otherwise.\n\ \n"); PyDoc_STRVAR(doc_ln,"\n\ -ln([context]) - Return the natural (base e) logarithm of the operand.\n\ +ln(context=None) - Return the natural (base e) logarithm of the operand.\n\ The function always uses the ROUND_HALF_EVEN mode and the result is\n\ correctly rounded.\n\ \n"); PyDoc_STRVAR(doc_log10,"\n\ -log10([context]) - Return the base ten logarithm of the operand.\n\ +log10(context=None) - Return the base ten logarithm of the operand.\n\ The function always uses the ROUND_HALF_EVEN mode and the result is\n\ correctly rounded.\n\ \n"); PyDoc_STRVAR(doc_logb,"\n\ -logb([context]) - For a non-zero number, return the adjusted exponent\n\ +logb(context=None) - For a non-zero number, return the adjusted exponent\n\ of the operand as a Decimal instance. If the operand is a zero, then\n\ Decimal('-Infinity') is returned and the DivisionByZero condition is\n\ raised. If the operand is an infinity then Decimal('Infinity') is returned.\n\ \n"); PyDoc_STRVAR(doc_logical_and,"\n\ -logical_and(other[, context]) - Return the digit-wise and of the two\n\ +logical_and(other, context=None) - Return the digit-wise and of the two\n\ (logical) operands.\n\ \n"); PyDoc_STRVAR(doc_logical_invert,"\n\ -logical_invert([context]) - Return the digit-wise inversion of the\n\ +logical_invert(context=None) - Return the digit-wise inversion of the\n\ (logical) operand.\n\ \n"); PyDoc_STRVAR(doc_logical_or,"\n\ -logical_or(other[, context]) - Return the digit-wise or of the two\n\ +logical_or(other, context=None) - Return the digit-wise or of the two\n\ (logical) operands.\n\ \n"); PyDoc_STRVAR(doc_logical_xor,"\n\ -logical_xor(other[, context]) - Return the digit-wise exclusive or of the\n\ +logical_xor(other, context=None) - Return the digit-wise exclusive or of the\n\ two (logical) operands.\n\ \n"); PyDoc_STRVAR(doc_max,"\n\ -max(other[, context]) - Maximum of self and other. If one operand is a quiet\n\ -NaN and the other is numeric, the numeric operand is returned.\n\ +max(other, context=None) - Maximum of self and other. If one operand is a\n\ +quiet NaN and the other is numeric, the numeric operand is returned.\n\ \n"); PyDoc_STRVAR(doc_max_mag,"\n\ -max_mag(other[, context]) - Similar to the max() method, but the comparison is\n\ -done using the absolute values of the operands.\n\ +max_mag(other, context=None) - Similar to the max() method, but the\n\ +comparison is done using the absolute values of the operands.\n\ \n"); PyDoc_STRVAR(doc_min,"\n\ -min(other[, context]) - Minimum of self and other. If one operand is a quiet\n\ -NaN and the other is numeric, the numeric operand is returned.\n\ +min(other, context=None) - Minimum of self and other. If one operand is a\n\ +quiet NaN and the other is numeric, the numeric operand is returned.\n\ \n"); PyDoc_STRVAR(doc_min_mag,"\n\ -min_mag(other[, context]) - Similar to the min() method, but the comparison is\n\ -done using the absolute values of the operands.\n\ +min_mag(other, context=None) - Similar to the min() method, but the\n\ +comparison is done using the absolute values of the operands.\n\ \n"); PyDoc_STRVAR(doc_next_minus,"\n\ -next_minus([context]) - Return the largest number representable in the given\n\ -context (or in the current default context if no context is given) that is\n\ -smaller than the given operand.\n\ +next_minus(context=None) - Return the largest number representable in the\n\ +given context (or in the current default context if no context is given) that\n\ +is smaller than the given operand.\n\ \n"); PyDoc_STRVAR(doc_next_plus,"\n\ -next_plus([context]) - Return the smallest number representable in the given\n\ -context (or in the current default context if no context is given) that is\n\ -larger than the given operand.\n\ +next_plus(context=None) - Return the smallest number representable in the\n\ +given context (or in the current default context if no context is given) that\n\ +is larger than the given operand.\n\ \n"); PyDoc_STRVAR(doc_next_toward,"\n\ -next_toward(other[, context]) - If the two operands are unequal, return the\n\ -number closest to the first operand in the direction of the second operand.\n\ +next_toward(other, context=None) - If the two operands are unequal, return\n\ +the number closest to the first operand in the direction of the second operand.\n\ If both operands are numerically equal, return a copy of the first operand\n\ with the sign set to be the same as the sign of the second operand.\n\ \n"); PyDoc_STRVAR(doc_normalize,"\n\ -normalize([context]) - Normalize the number by stripping the rightmost trailing\n\ -zeros and converting any result equal to Decimal('0') to Decimal('0e0'). Used\n\ -for producing canonical values for members of an equivalence class. For example,\n\ -Decimal('32.100') and Decimal('0.321000e+2') both normalize to the equivalent\n\ -value Decimal('32.1').\n\ +normalize(context=None) - Normalize the number by stripping the rightmost\n\ +trailing zeros and converting any result equal to Decimal('0') to Decimal('0e0').\n\ +Used for producing canonical values for members of an equivalence class. For\n\ +example, Decimal('32.100') and Decimal('0.321000e+2') both normalize to the\n\ +equivalent value Decimal('32.1').\n\ \n"); PyDoc_STRVAR(doc_number_class,"\n\ -number_class([context]) - Return a string describing the class of the operand.\n\ -The returned value is one of the following ten strings:\n\ +number_class(context=None) - Return a string describing the class of the\n\ +operand. The returned value is one of the following ten strings:\n\ \n\ * '-Infinity', indicating that the operand is negative infinity.\n\ * '-Normal', indicating that the operand is a negative normal number.\n\ @@ -324,8 +332,8 @@ The returned value is one of the following ten strings:\n\ \n"); PyDoc_STRVAR(doc_quantize,"\n\ -quantize(exp[, rounding[, context]]) - Return a value equal to the first\n\ -operand after rounding and having the exponent of the second operand.\n\ +quantize(exp, rounding=None, context=None) - Return a value equal to the\n\ +first operand after rounding and having the exponent of the second operand.\n\ \n\ >>> Decimal('1.41421356').quantize(Decimal('1.000'))\n\ Decimal('1.414')\n\ @@ -350,16 +358,18 @@ all its arithmetic. Included for compatibility with the specification.\n\ \n"); PyDoc_STRVAR(doc_remainder_near,"\n\ -remainder_near(other[, context]) - Compute the modulo as either a positive\n\ -or negative value depending on which is closest to zero. For instance,\n\ -Decimal(10).remainder_near(6) returns Decimal('-2'), which is closer to zero\n\ -than Decimal('4').\n\ +remainder_near(other, context=None) - Return the remainder from dividing\n\ +self by other. This differs from self % other in that the sign of the\n\ +remainder is chosen so as to minimize its absolute value. More precisely, the\n\ +return value is self - n * other where n is the integer nearest to the exact\n\ +value of self / other, and if two integers are equally near then the even one\n\ +is chosen.\n\ \n\ -If both are equally close, the one chosen will have the same sign as self.\n\ +If the result is zero then its sign will be the sign of self.\n\ \n"); PyDoc_STRVAR(doc_rotate,"\n\ -rotate(other[, context]) - Return the result of rotating the digits of the\n\ +rotate(other, context=None) - Return the result of rotating the digits of the\n\ first operand by an amount specified by the second operand. The second operand\n\ must be an integer in the range -precision through precision. The absolute\n\ value of the second operand gives the number of places to rotate. If the second\n\ @@ -370,18 +380,22 @@ unchanged.\n\ \n"); PyDoc_STRVAR(doc_same_quantum,"\n\ -same_quantum(other[, context]) - Test whether self and other have the\n\ +same_quantum(other, context=None) - Test whether self and other have the\n\ same exponent or whether both are NaN.\n\ +\n\ +This operation is unaffected by context and is quiet: no flags are changed\n\ +and no rounding is performed. As an exception, the C version may raise\n\ +InvalidOperation if the second operand cannot be converted exactly.\n\ \n"); PyDoc_STRVAR(doc_scaleb,"\n\ -scaleb(other[, context]) - Return the first operand with the exponent adjusted\n\ -the second. Equivalently, return the first operand multiplied by 10**other.\n\ -The second operand must be an integer.\n\ +scaleb(other, context=None) - Return the first operand with the exponent\n\ +adjusted the second. Equivalently, return the first operand multiplied by\n\ +10**other. The second operand must be an integer.\n\ \n"); PyDoc_STRVAR(doc_shift,"\n\ -shift(other[, context]) - Return the result of shifting the digits of\n\ +shift(other, context=None) - Return the result of shifting the digits of\n\ the first operand by an amount specified by the second operand. The second\n\ operand must be an integer in the range -precision through precision. The\n\ absolute value of the second operand gives the number of places to shift.\n\ @@ -391,36 +405,40 @@ The sign and exponent of the first operand are unchanged.\n\ \n"); PyDoc_STRVAR(doc_sqrt,"\n\ -sqrt([context]) - Return the square root of the argument to full precision.\n\ +sqrt(context=None) - Return the square root of the argument to full precision.\n\ The result is correctly rounded using the ROUND_HALF_EVEN rounding mode.\n\ \n"); PyDoc_STRVAR(doc_to_eng_string,"\n\ -to_eng_string([context]) - Convert to an engineering-type string.\n\ -Engineering notation has an exponent which is a multiple of 3, so\n\ -there are up to 3 digits left of the decimal place. For example,\n\ -Decimal('123E+1') is converted to Decimal('1.23E+3')\n\ +to_eng_string(context=None) - Convert to an engineering-type string.\n\ +Engineering notation has an exponent which is a multiple of 3, so there\n\ +are up to 3 digits left of the decimal place. For example, Decimal('123E+1')\n\ +is converted to Decimal('1.23E+3').\n\ +\n\ +The value of context.capitals determines whether the exponent sign is lower\n\ +or upper case. Otherwise, the context does not affect the operation.\n\ \n"); PyDoc_STRVAR(doc_to_integral,"\n\ -to_integral([rounding[, context]]) - Identical to the to_integral_value()\n\ -method. The to_integral name has been kept for compatibility with older\n\ -versions.\n\ +to_integral(rounding=None, context=None) - Identical to the\n\ +to_integral_value() method. The to_integral() name has been kept\n\ +for compatibility with older versions.\n\ \n"); PyDoc_STRVAR(doc_to_integral_exact,"\n\ -to_integral_exact([rounding[, context]]) - Round to the nearest integer,\n\ -signaling Inexact or Rounded as appropriate if rounding occurs. The rounding\n\ -mode is determined by the rounding parameter if given, else by the given\n\ -context. If neither parameter is given, then the rounding mode of the current\n\ -default context is used.\n\ +to_integral_exact(rounding=None, context=None) - Round to the nearest\n\ +integer, signaling Inexact or Rounded as appropriate if rounding occurs.\n\ +The rounding mode is determined by the rounding parameter if given, else\n\ +by the given context. If neither parameter is given, then the rounding mode\n\ +of the current default context is used.\n\ \n"); PyDoc_STRVAR(doc_to_integral_value,"\n\ -to_integral_value([rounding[, context]]) - Round to the nearest integer without\n\ -signaling Inexact or Rounded. The rounding mode is determined by the rounding\n\ -parameter if given, else by the given context. If neither parameter is given,\n\ -then the rounding mode of the current default context is used.\n\ +to_integral_value(rounding=None, context=None) - Round to the nearest\n\ +integer without signaling Inexact or Rounded. The rounding mode is determined\n\ +by the rounding parameter if given, else by the given context. If neither\n\ +parameter is given, then the rounding mode of the current default context is\n\ +used.\n\ \n"); diff --git a/Modules/_decimal/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py index c8da4d02b73..751ba575de0 100644 --- a/Modules/_decimal/tests/deccheck.py +++ b/Modules/_decimal/tests/deccheck.py @@ -36,6 +36,7 @@ from copy import copy from collections import defaultdict from test.support import import_fresh_module from randdec import randfloat, all_unary, all_binary, all_ternary +from randdec import unary_optarg, binary_optarg, ternary_optarg from formathelper import rand_format, rand_locale C = import_fresh_module('decimal', fresh=['_decimal']) @@ -834,6 +835,17 @@ def test_unary(method, prec, exp_range, restricted_range, itr, stat): except VerifyError as err: log(err) + if not method.startswith('__'): + for op in unary_optarg(prec, exp_range, itr): + t = TestSet(method, op) + try: + if not convert(t): + continue + callfuncs(t) + verify(t, stat) + except VerifyError as err: + log(err) + def test_binary(method, prec, exp_range, restricted_range, itr, stat): """Iterate a binary function through many test cases.""" if method in BinaryRestricted: @@ -848,6 +860,17 @@ def test_binary(method, prec, exp_range, restricted_range, itr, stat): except VerifyError as err: log(err) + if not method.startswith('__'): + for op in binary_optarg(prec, exp_range, itr): + t = TestSet(method, op) + try: + if not convert(t): + continue + callfuncs(t) + verify(t, stat) + except VerifyError as err: + log(err) + def test_ternary(method, prec, exp_range, restricted_range, itr, stat): """Iterate a ternary function through many test cases.""" if method in TernaryRestricted: @@ -862,6 +885,17 @@ def test_ternary(method, prec, exp_range, restricted_range, itr, stat): except VerifyError as err: log(err) + if not method.startswith('__'): + for op in ternary_optarg(prec, exp_range, itr): + t = TestSet(method, op) + try: + if not convert(t): + continue + callfuncs(t) + verify(t, stat) + except VerifyError as err: + log(err) + def test_format(method, prec, exp_range, restricted_range, itr, stat): """Iterate the __format__ method through many test cases.""" for op in all_unary(prec, exp_range, itr): diff --git a/Modules/_decimal/tests/randdec.py b/Modules/_decimal/tests/randdec.py index 80858bfcb5b..ca0f0d17753 100644 --- a/Modules/_decimal/tests/randdec.py +++ b/Modules/_decimal/tests/randdec.py @@ -527,6 +527,11 @@ def all_unary(prec, exp_range, itr): for _ in range(100): yield (randtuple(prec, exp_range),) +def unary_optarg(prec, exp_range, itr): + for _ in range(100): + yield randdec(prec, exp_range), None + yield randdec(prec, exp_range), None, None + def all_binary(prec, exp_range, itr): for a, b in bin_close_to_pow10(prec, exp_range, itr): yield a, b @@ -543,6 +548,11 @@ def all_binary(prec, exp_range, itr): for _ in range(100): yield randdec(prec, exp_range), randdec(prec, exp_range) +def binary_optarg(prec, exp_range, itr): + for _ in range(100): + yield randdec(prec, exp_range), randdec(prec, exp_range), None + yield randdec(prec, exp_range), randdec(prec, exp_range), None, None + def all_ternary(prec, exp_range, itr): for a, b, c in tern_close_numbers(prec, exp_range, -exp_range, itr): yield a, b, c @@ -557,3 +567,11 @@ def all_ternary(prec, exp_range, itr): b = randdec(prec, 2*exp_range) c = randdec(prec, 2*exp_range) yield a, b, c + +def ternary_optarg(prec, exp_range, itr): + for _ in range(100): + a = randdec(prec, 2*exp_range) + b = randdec(prec, 2*exp_range) + c = randdec(prec, 2*exp_range) + yield a, b, c, None + yield a, b, c, None, None