diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 1d987b8d1f5..3fa006bec22 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -1577,7 +1577,7 @@ The following table summarizes the hierarchy of signals:: InvalidOperation Rounded Subnormal - FloatOperation + FloatOperation(DecimalException, exceptions.TypeError) .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Lib/decimal.py b/Lib/decimal.py index b6f66aba852..6bd655bdb75 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -391,7 +391,7 @@ class Underflow(Inexact, Rounded, Subnormal): In all cases, Inexact, Rounded, and Subnormal will also be raised. """ -class FloatOperation(DecimalException): +class FloatOperation(DecimalException, TypeError): """Enable stricter semantics for mixing floats and Decimals. If the signal is not trapped (default), mixing floats and Decimals is diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index af31b385980..4a352e51073 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2378,6 +2378,46 @@ class PythonAPItests(unittest.TestCase): self.assertRaises(TypeError, D("-1").copy_abs, context=xc) self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + def test_exception_hierarchy(self): + + decimal = self.decimal + DecimalException = decimal.DecimalException + InvalidOperation = decimal.InvalidOperation + FloatOperation = decimal.FloatOperation + DivisionByZero = decimal.DivisionByZero + Overflow = decimal.Overflow + Underflow = decimal.Underflow + Subnormal = decimal.Subnormal + Inexact = decimal.Inexact + Rounded = decimal.Rounded + Clamped = decimal.Clamped + + self.assertTrue(issubclass(DecimalException, ArithmeticError)) + + self.assertTrue(issubclass(InvalidOperation, DecimalException)) + self.assertTrue(issubclass(FloatOperation, DecimalException)) + self.assertTrue(issubclass(FloatOperation, TypeError)) + self.assertTrue(issubclass(DivisionByZero, DecimalException)) + self.assertTrue(issubclass(DivisionByZero, ZeroDivisionError)) + self.assertTrue(issubclass(Overflow, Rounded)) + self.assertTrue(issubclass(Overflow, Inexact)) + self.assertTrue(issubclass(Overflow, DecimalException)) + self.assertTrue(issubclass(Underflow, Inexact)) + self.assertTrue(issubclass(Underflow, Rounded)) + self.assertTrue(issubclass(Underflow, Subnormal)) + self.assertTrue(issubclass(Underflow, DecimalException)) + + self.assertTrue(issubclass(Subnormal, DecimalException)) + self.assertTrue(issubclass(Inexact, DecimalException)) + self.assertTrue(issubclass(Rounded, DecimalException)) + self.assertTrue(issubclass(Clamped, DecimalException)) + + self.assertTrue(issubclass(decimal.ConversionSyntax, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionImpossible, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionUndefined, InvalidOperation)) + self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError)) + self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation)) + class CPythonAPItests(PythonAPItests): decimal = C class PyPythonAPItests(PythonAPItests): diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index ad370923d5b..aa39de1ba81 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -143,7 +143,10 @@ typedef struct { /* Top level Exception; inherits from ArithmeticError */ static PyObject *DecimalException = NULL; -/* Exceptions that correspond to IEEE signals; inherit from DecimalException */ +/* Exceptions that correspond to IEEE signals */ +#define SUBNORMAL 5 +#define INEXACT 6 +#define ROUNDED 7 #define SIGNAL_MAP_LEN 9 static DecCondMap signal_map[] = { {"InvalidOperation", "decimal.InvalidOperation", MPD_IEEE_Invalid_operation, NULL}, @@ -5403,9 +5406,38 @@ PyInit__decimal(void) ASSIGN_PTR(SignalTuple, PyTuple_New(SIGNAL_MAP_LEN)); /* Add exceptions that correspond to IEEE signals */ - for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) { - ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, - DecimalException, NULL)); + for (i = SIGNAL_MAP_LEN-1; i >= 0; i--) { + PyObject *base; + + cm = signal_map + i; + + switch (cm->flag) { + case MPD_Float_operation: + base = PyTuple_Pack(2, DecimalException, PyExc_TypeError); + break; + case MPD_Division_by_zero: + base = PyTuple_Pack(2, DecimalException, PyExc_ZeroDivisionError); + break; + case MPD_Overflow: + base = PyTuple_Pack(2, signal_map[INEXACT].ex, + signal_map[ROUNDED].ex); + break; + case MPD_Underflow: + base = PyTuple_Pack(3, signal_map[INEXACT].ex, + signal_map[ROUNDED].ex, + signal_map[SUBNORMAL].ex); + break; + default: + base = PyTuple_Pack(1, DecimalException); + break; + } + + if (base == NULL) { + goto error; + } + + ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, base, NULL)); + Py_DECREF(base); /* add to module */ Py_INCREF(cm->ex); @@ -5425,8 +5457,20 @@ PyInit__decimal(void) /* Add remaining exceptions, inherit from InvalidOperation */ for (cm = cond_map+1; cm->name != NULL; cm++) { - ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, - signal_map[0].ex, NULL)); + PyObject *base; + if (cm->flag == MPD_Division_undefined) { + base = PyTuple_Pack(2, signal_map[0].ex, PyExc_ZeroDivisionError); + } + else { + base = PyTuple_Pack(1, signal_map[0].ex); + } + if (base == NULL) { + goto error; + } + + ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, base, NULL)); + Py_DECREF(base); + Py_INCREF(cm->ex); CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex)); } @@ -5472,6 +5516,7 @@ PyInit__decimal(void) for (ssize_cm = ssize_constants; ssize_cm->name != NULL; ssize_cm++) { ASSIGN_PTR(obj, PyLong_FromSsize_t(ssize_cm->val)); CHECK_INT(PyModule_AddObject(m, ssize_cm->name, obj)); + obj = NULL; } /* Init int constants */ @@ -5488,23 +5533,23 @@ PyInit__decimal(void) error: - Py_XDECREF(obj); /* GCOV_NOT_REACHED */ - Py_XDECREF(numbers); /* GCOV_NOT_REACHED */ - Py_XDECREF(Number); /* GCOV_NOT_REACHED */ - Py_XDECREF(Rational); /* GCOV_NOT_REACHED */ - Py_XDECREF(collections); /* GCOV_NOT_REACHED */ - Py_XDECREF(MutableMapping); /* GCOV_NOT_REACHED */ - Py_XDECREF(SignalTuple); /* GCOV_NOT_REACHED */ - Py_XDECREF(DecimalTuple); /* GCOV_NOT_REACHED */ + Py_CLEAR(obj); /* GCOV_NOT_REACHED */ + Py_CLEAR(numbers); /* GCOV_NOT_REACHED */ + Py_CLEAR(Number); /* GCOV_NOT_REACHED */ + Py_CLEAR(Rational); /* GCOV_NOT_REACHED */ + Py_CLEAR(collections); /* GCOV_NOT_REACHED */ + Py_CLEAR(MutableMapping); /* GCOV_NOT_REACHED */ + Py_CLEAR(SignalTuple); /* GCOV_NOT_REACHED */ + Py_CLEAR(DecimalTuple); /* GCOV_NOT_REACHED */ #ifdef WITHOUT_THREADS - Py_XDECREF(module_context); /* GCOV_NOT_REACHED */ + Py_CLEAR(module_context); /* GCOV_NOT_REACHED */ #else - Py_XDECREF(default_context_template); /* GCOV_NOT_REACHED */ - Py_XDECREF(tls_context_key); /* GCOV_NOT_REACHED */ + Py_CLEAR(default_context_template); /* GCOV_NOT_REACHED */ + Py_CLEAR(tls_context_key); /* GCOV_NOT_REACHED */ #endif - Py_XDECREF(basic_context_template); /* GCOV_NOT_REACHED */ - Py_XDECREF(extended_context_template); /* GCOV_NOT_REACHED */ - Py_XDECREF(m); /* GCOV_NOT_REACHED */ + Py_CLEAR(basic_context_template); /* GCOV_NOT_REACHED */ + Py_CLEAR(extended_context_template); /* GCOV_NOT_REACHED */ + Py_CLEAR(m); /* GCOV_NOT_REACHED */ return NULL; /* GCOV_NOT_REACHED */ }