diff --git a/Lib/decimal.py b/Lib/decimal.py --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -2594,7 +2594,7 @@ 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. @@ -2932,7 +2932,7 @@ 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 @@ -3005,7 +3005,7 @@ 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. @@ -3027,7 +3027,7 @@ 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, diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2089,6 +2089,248 @@ 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,12 @@ 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(context=xc), 1) + self.assertRaises(TypeError, D("-1").copy_abs, context=xc) + self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + if self.decimal == C: + self.assertRaises(TypeError, D(1).canonical, context="x") + self.assertRaises(TypeError, D(1).canonical, xyz="x") def test_exception_hierarchy(self): @@ -4593,6 +4833,9 @@ # 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 +5069,50 @@ 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/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1486,7 +1486,10 @@ } #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 @@ * 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; } - CONTEXT_CHECK_VA(local); + if (local == Py_None) { + local = global; + } + else if (!PyDecContext_Check(local)) { + PyErr_SetString(PyExc_TypeError, + "optional argument must be a context"); + return NULL; + } self = PyObject_New(PyDecContextManagerObject, &PyDecContextManager_Type); @@ -2749,9 +2759,8 @@ { static char *kwlist[] = {"value", "context", NULL}; PyObject *v = NULL; - PyObject *context; - - CURRENT_CONTEXT(context); + PyObject *context = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &v, &context)) { return NULL; @@ -3315,20 +3324,23 @@ { 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 @@ { 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 @@ 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 @@ { \ 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 @@ 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 @@ 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 @@ 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; \ @@ -3992,8 +3974,17 @@ } static PyObject * -dec_canonical(PyObject *self, PyObject *dummy UNUSED) +dec_canonical(PyObject *self, PyObject *args, PyObject *kwds) { + /* Unused: for compatibility with decimal.py */ + static char *kwlist[] = {"context", NULL}; + PyObject *context = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + Py_INCREF(self); return self; } @@ -4019,9 +4010,45 @@ 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 +4058,9 @@ 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 +4076,10 @@ { 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 +4106,12 @@ 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 +4141,11 @@ 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; - - CURRENT_CONTEXT(context); + PyObject *context = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &other, &context)) { return NULL; @@ -4148,22 +4173,25 @@ 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); } @@ -4580,13 +4608,13 @@ /* Unary functions, no context arg */ { "adjusted", dec_mpd_adjexp, METH_NOARGS, doc_adjusted }, - { "canonical", dec_canonical, METH_NOARGS, doc_canonical }, + { "canonical", (PyCFunction)dec_canonical, METH_VARARGS|METH_KEYWORDS, doc_canonical }, { "conjugate", dec_conjugate, METH_NOARGS, doc_conjugate }, { "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 +4944,7 @@ 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 +4956,7 @@ 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 +5389,7 @@ { { "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/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py --- a/Modules/_decimal/tests/deccheck.py +++ b/Modules/_decimal/tests/deccheck.py @@ -36,6 +36,7 @@ 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 @@ 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 @@ 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 @@ 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 --- a/Modules/_decimal/tests/randdec.py +++ b/Modules/_decimal/tests/randdec.py @@ -527,6 +527,11 @@ 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 @@ 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 @@ 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