From 85371fccc9ddd20019853c02131befdff2efe219 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 15 May 2024 12:17:16 +0300 Subject: [PATCH 1/4] gh-119057: Use better error message for `x // 0` --- Doc/howto/logging-cookbook.rst | 2 +- Lib/test/test_builtin.py | 5 +++++ Lib/test/test_doctest/test_doctest.py | 8 ++++---- Lib/test/test_generators.py | 2 +- Lib/test/test_genexps.py | 2 +- .../2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst | 2 ++ Objects/longobject.c | 13 ++++++++----- 7 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 60d88204b795f6..340e61de0e8d31 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2950,7 +2950,7 @@ When run, this produces a file with exactly two lines: .. code-block:: none 28/01/2015 07:21:23|INFO|Sample message| - 28/01/2015 07:21:23|ERROR|ZeroDivisionError: integer division or modulo by zero|'Traceback (most recent call last):\n File "logtest7.py", line 30, in main\n x = 1 / 0\nZeroDivisionError: integer division or modulo by zero'| + 28/01/2015 07:21:23|ERROR|ZeroDivisionError: integer division by zero|'Traceback (most recent call last):\n File "logtest7.py", line 30, in main\n x = 1 / 0\nZeroDivisionError: integer division by zero'| While the above treatment is simplistic, it points the way to how exception information can be formatted to your liking. The :mod:`traceback` module may be diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index d7ba58847a2992..98406cc28995e2 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -662,6 +662,11 @@ def test_divmod(self): self.assertAlmostEqual(result[1], exp_result[1]) self.assertRaises(TypeError, divmod) + self.assertRaisesRegex( + ZeroDivisionError, + "integer division or modulo by zero", + divmod, 1, 0, + ) def test_eval(self): self.assertEqual(eval('1+1'), 2) diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 286c3ecfbc9239..3f8c6916f39e1e 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -1035,7 +1035,7 @@ def exceptions(): r""" ... >>> x = 12 ... >>> print(x//0) ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero + ... ZeroDivisionError: integer floor division by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -1052,7 +1052,7 @@ def exceptions(): r""" ... >>> print('pre-exception output', x//0) ... pre-exception output ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero + ... ZeroDivisionError: integer floor division by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -1063,7 +1063,7 @@ def exceptions(): r""" print('pre-exception output', x//0) Exception raised: ... - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: integer floor division by zero TestResults(failed=1, attempted=2) Exception messages may contain newlines: @@ -1258,7 +1258,7 @@ def exceptions(): r""" Exception raised: Traceback (most recent call last): ... - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: integer floor division by zero TestResults(failed=1, attempted=1) >>> _colorize.COLORIZE = save_colorize diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 6d36df2c7413e0..cf9bed1df7a894 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -907,7 +907,7 @@ def b(): File "", line 1, in ? File "", line 2, in g File "", line 2, in f - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: integer floor division by zero >>> next(k) # and the generator cannot be resumed Traceback (most recent call last): File "", line 1, in ? diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index 4f2d3cdcc7943e..aa3c87cefd2622 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -223,7 +223,7 @@ next(g) File "", line 1, in g = (10 // i for i in (5, 0, 2)) - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: integer floor division by zero >>> next(g) Traceback (most recent call last): File "", line 1, in -toplevel- diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst new file mode 100644 index 00000000000000..c6c781277c4953 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst @@ -0,0 +1,2 @@ +Improve error message when using ``// 0`` from "integer division or modulo +by zero" to "integer floor division by zero" which better represents the division. diff --git a/Objects/longobject.c b/Objects/longobject.c index ce3fd6b711dcd4..4aca8da91dda35 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3103,14 +3103,17 @@ static PyObject *long_long(PyObject *v); static int long_divrem(PyLongObject *a, PyLongObject *b, - PyLongObject **pdiv, PyLongObject **prem) + PyLongObject **pdiv, PyLongObject **prem, int divonly) { Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b); PyLongObject *z; if (size_b == 0) { - PyErr_SetString(PyExc_ZeroDivisionError, - "integer division or modulo by zero"); + PyErr_SetString( + PyExc_ZeroDivisionError, + divonly ? + "integer floor division by zero" : + "integer division or modulo by zero"); return -1; } if (size_a < size_b || @@ -4367,7 +4370,7 @@ l_divmod(PyLongObject *v, PyLongObject *w, return pylong_int_divmod(v, w, pdiv, pmod); } #endif - if (long_divrem(v, w, &div, &mod) < 0) + if (long_divrem(v, w, &div, &mod, pmod == NULL) < 0) return -1; if ((_PyLong_IsNegative(mod) && _PyLong_IsPositive(w)) || (_PyLong_IsPositive(mod) && _PyLong_IsNegative(w))) { @@ -5978,7 +5981,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) /* Do a and b have different signs? If so, quotient is negative. */ quo_is_neg = (_PyLong_IsNegative((PyLongObject *)a)) != (_PyLong_IsNegative((PyLongObject *)b)); - if (long_divrem((PyLongObject*)a, (PyLongObject*)b, &quo, &rem) < 0) + if (long_divrem((PyLongObject*)a, (PyLongObject*)b, &quo, &rem, 0) < 0) goto error; /* compare twice the remainder with the divisor, to see From aa358c4ce1a88b4934cb0d9e7fcdadae5c654fd8 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 16 May 2024 11:05:01 +0300 Subject: [PATCH 2/4] Change error messages for `/`, `//`, `%` and `0 ** -1` --- Lib/_pylong.py | 2 +- Lib/test/test_builtin.py | 7 ++++++- Lib/test/test_doctest/test_doctest.py | 8 ++++---- Lib/test/test_generators.py | 2 +- Lib/test/test_genexps.py | 2 +- .../2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst | 6 ++++-- Objects/complexobject.c | 4 ++-- Objects/floatobject.c | 11 +++++------ Objects/longobject.c | 14 +++++--------- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/Lib/_pylong.py b/Lib/_pylong.py index 4970eb3fa67b2b..7742aa3139fe16 100644 --- a/Lib/_pylong.py +++ b/Lib/_pylong.py @@ -352,7 +352,7 @@ def int_divmod(a, b): Its time complexity is O(n**1.58), where n = #bits(a) + #bits(b). """ if b == 0: - raise ZeroDivisionError + raise ZeroDivisionError('division by zero') elif b < 0: q, r = int_divmod(-a, -b) return q, -r diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 98406cc28995e2..9ff0f488dc4fa9 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -664,9 +664,14 @@ def test_divmod(self): self.assertRaises(TypeError, divmod) self.assertRaisesRegex( ZeroDivisionError, - "integer division or modulo by zero", + "division by zero", divmod, 1, 0, ) + self.assertRaisesRegex( + ZeroDivisionError, + "division by zero", + divmod, 0.0, 0, + ) def test_eval(self): self.assertEqual(eval('1+1'), 2) diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 3f8c6916f39e1e..b25d57ceeae6aa 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -1035,7 +1035,7 @@ def exceptions(): r""" ... >>> x = 12 ... >>> print(x//0) ... Traceback (most recent call last): - ... ZeroDivisionError: integer floor division by zero + ... ZeroDivisionError: division by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -1052,7 +1052,7 @@ def exceptions(): r""" ... >>> print('pre-exception output', x//0) ... pre-exception output ... Traceback (most recent call last): - ... ZeroDivisionError: integer floor division by zero + ... ZeroDivisionError: division by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -1063,7 +1063,7 @@ def exceptions(): r""" print('pre-exception output', x//0) Exception raised: ... - ZeroDivisionError: integer floor division by zero + ZeroDivisionError: division by zero TestResults(failed=1, attempted=2) Exception messages may contain newlines: @@ -1258,7 +1258,7 @@ def exceptions(): r""" Exception raised: Traceback (most recent call last): ... - ZeroDivisionError: integer floor division by zero + ZeroDivisionError: division by zero TestResults(failed=1, attempted=1) >>> _colorize.COLORIZE = save_colorize diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index cf9bed1df7a894..4598e62122b09c 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -907,7 +907,7 @@ def b(): File "", line 1, in ? File "", line 2, in g File "", line 2, in f - ZeroDivisionError: integer floor division by zero + ZeroDivisionError: division by zero >>> next(k) # and the generator cannot be resumed Traceback (most recent call last): File "", line 1, in ? diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index aa3c87cefd2622..7fb58a67368576 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -223,7 +223,7 @@ next(g) File "", line 1, in g = (10 // i for i in (5, 0, 2)) - ZeroDivisionError: integer floor division by zero + ZeroDivisionError: division by zero >>> next(g) Traceback (most recent call last): File "", line 1, in -toplevel- diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst index c6c781277c4953..d252888906c348 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-15-12-15-58.gh-issue-119057.P3G9G2.rst @@ -1,2 +1,4 @@ -Improve error message when using ``// 0`` from "integer division or modulo -by zero" to "integer floor division by zero" which better represents the division. +Improve :exc:`ZeroDivisionError` error message. +Now, all error messages are harmonized: all ``/``, ``//``, and ``%`` +operations just use "division by zero" message. +And ``0 ** -1`` operation uses "zero to a negative power". diff --git a/Objects/complexobject.c b/Objects/complexobject.c index d8b0e84da5df4a..ac5782e44e8b06 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -523,7 +523,7 @@ complex_div(PyObject *v, PyObject *w) errno = 0; quot = _Py_c_quot(a, b); if (errno == EDOM) { - PyErr_SetString(PyExc_ZeroDivisionError, "complex division by zero"); + PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); return NULL; } return PyComplex_FromCComplex(quot); @@ -554,7 +554,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) _Py_ADJUST_ERANGE2(p.real, p.imag); if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, - "0.0 to a negative or complex power"); + "zero to a negative or complex power"); return NULL; } else if (errno == ERANGE) { diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 96227f2cf7d76f..ba67e881bb1cfa 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -623,7 +623,7 @@ float_div(PyObject *v, PyObject *w) CONVERT_TO_DOUBLE(w, b); if (b == 0.0) { PyErr_SetString(PyExc_ZeroDivisionError, - "float division by zero"); + "division by zero"); return NULL; } a = a / b; @@ -639,7 +639,7 @@ float_rem(PyObject *v, PyObject *w) CONVERT_TO_DOUBLE(w, wx); if (wx == 0.0) { PyErr_SetString(PyExc_ZeroDivisionError, - "float modulo by zero"); + "division by zero"); return NULL; } mod = fmod(vx, wx); @@ -704,7 +704,7 @@ float_divmod(PyObject *v, PyObject *w) CONVERT_TO_DOUBLE(v, vx); CONVERT_TO_DOUBLE(w, wx); if (wx == 0.0) { - PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()"); + PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); return NULL; } _float_div_mod(vx, wx, &floordiv, &mod); @@ -719,7 +719,7 @@ float_floor_div(PyObject *v, PyObject *w) CONVERT_TO_DOUBLE(v, vx); CONVERT_TO_DOUBLE(w, wx); if (wx == 0.0) { - PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero"); + PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); return NULL; } _float_div_mod(vx, wx, &floordiv, &mod); @@ -788,8 +788,7 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) int iw_is_odd = DOUBLE_IS_ODD_INTEGER(iw); if (iw < 0.0) { PyErr_SetString(PyExc_ZeroDivisionError, - "0.0 cannot be raised to a " - "negative power"); + "zero to a negative power"); return NULL; } /* use correct sign if iw is odd */ diff --git a/Objects/longobject.c b/Objects/longobject.c index 4aca8da91dda35..42c4de6d85d31f 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3103,17 +3103,13 @@ static PyObject *long_long(PyObject *v); static int long_divrem(PyLongObject *a, PyLongObject *b, - PyLongObject **pdiv, PyLongObject **prem, int divonly) + PyLongObject **pdiv, PyLongObject **prem) { Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b); PyLongObject *z; if (size_b == 0) { - PyErr_SetString( - PyExc_ZeroDivisionError, - divonly ? - "integer floor division by zero" : - "integer division or modulo by zero"); + PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); return -1; } if (size_a < size_b || @@ -3176,7 +3172,7 @@ long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem) if (size_b == 0) { PyErr_SetString(PyExc_ZeroDivisionError, - "integer modulo by zero"); + "division by zero"); return -1; } if (size_a < size_b || @@ -4370,7 +4366,7 @@ l_divmod(PyLongObject *v, PyLongObject *w, return pylong_int_divmod(v, w, pdiv, pmod); } #endif - if (long_divrem(v, w, &div, &mod, pmod == NULL) < 0) + if (long_divrem(v, w, &div, &mod) < 0) return -1; if ((_PyLong_IsNegative(mod) && _PyLong_IsPositive(w)) || (_PyLong_IsPositive(mod) && _PyLong_IsNegative(w))) { @@ -5981,7 +5977,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) /* Do a and b have different signs? If so, quotient is negative. */ quo_is_neg = (_PyLong_IsNegative((PyLongObject *)a)) != (_PyLong_IsNegative((PyLongObject *)b)); - if (long_divrem((PyLongObject*)a, (PyLongObject*)b, &quo, &rem, 0) < 0) + if (long_divrem((PyLongObject*)a, (PyLongObject*)b, &quo, &rem) < 0) goto error; /* compare twice the remainder with the divisor, to see From c7f202a43df71465a18f46b55cb357e7cf66e2cb Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 16 May 2024 12:01:27 +0300 Subject: [PATCH 3/4] Fix CI --- Lib/test/mathdata/ieee754.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/mathdata/ieee754.txt b/Lib/test/mathdata/ieee754.txt index a8b8a0a2148f00..0bc45603b8b18a 100644 --- a/Lib/test/mathdata/ieee754.txt +++ b/Lib/test/mathdata/ieee754.txt @@ -116,7 +116,7 @@ inf >>> 0 ** -1 Traceback (most recent call last): ... -ZeroDivisionError: 0.0 cannot be raised to a negative power +ZeroDivisionError: zero to a negative power >>> pow(0, NAN) nan From 4aa317892e7ba48b6edec8595b652a064319cc10 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 16 May 2024 12:07:45 +0300 Subject: [PATCH 4/4] Fix CI --- Doc/howto/logging-cookbook.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 340e61de0e8d31..3ed2dd6251afe9 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2950,7 +2950,7 @@ When run, this produces a file with exactly two lines: .. code-block:: none 28/01/2015 07:21:23|INFO|Sample message| - 28/01/2015 07:21:23|ERROR|ZeroDivisionError: integer division by zero|'Traceback (most recent call last):\n File "logtest7.py", line 30, in main\n x = 1 / 0\nZeroDivisionError: integer division by zero'| + 28/01/2015 07:21:23|ERROR|ZeroDivisionError: division by zero|'Traceback (most recent call last):\n File "logtest7.py", line 30, in main\n x = 1 / 0\nZeroDivisionError: division by zero'| While the above treatment is simplistic, it points the way to how exception information can be formatted to your liking. The :mod:`traceback` module may be