From 85edd1525b3684cf47b40b320a0f3aba441cf7e3 Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Wed, 1 Feb 2023 11:02:44 +0800 Subject: [PATCH 1/8] Customize error messages in the math module. --- Modules/mathmodule.c | 74 ++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index e6cdb3bae1ecff..468fc1898270bb 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1054,7 +1054,8 @@ is_error(double x) static PyObject * math_1_to_whatever(PyObject *arg, double (*func) (double), PyObject *(*from_double_func) (double), - int can_overflow) + int can_overflow, + const char *err_msg) { double x, r; x = PyFloat_AsDouble(arg); @@ -1063,8 +1064,7 @@ math_1_to_whatever(PyObject *arg, double (*func) (double), errno = 0; r = (*func)(x); if (Py_IS_NAN(r) && !Py_IS_NAN(x)) { - PyErr_SetString(PyExc_ValueError, - "math domain error"); /* invalid arg */ + PyErr_Format(PyExc_ValueError, err_msg, arg); /* invalid arg */ return NULL; } if (Py_IS_INFINITY(r) && Py_IS_FINITE(x)) { @@ -1129,9 +1129,9 @@ math_1a(PyObject *arg, double (*func) (double)) */ static PyObject * -math_1(PyObject *arg, double (*func) (double), int can_overflow) +math_1(PyObject *arg, double (*func) (double), int can_overflow, const char *err_msg) { - return math_1_to_whatever(arg, func, PyFloat_FromDouble, can_overflow); + return math_1_to_whatever(arg, func, PyFloat_FromDouble, can_overflow, err_msg); } static PyObject * @@ -1169,9 +1169,9 @@ math_2(PyObject *const *args, Py_ssize_t nargs, return PyFloat_FromDouble(r); } -#define FUNC1(funcname, func, can_overflow, docstring) \ +#define FUNC1(funcname, func, can_overflow, docstring, err_msg) \ static PyObject * math_##funcname(PyObject *self, PyObject *args) { \ - return math_1(args, func, can_overflow); \ + return math_1(args, func, can_overflow, err_msg); \ }\ PyDoc_STRVAR(math_##funcname##_doc, docstring); @@ -1190,31 +1190,38 @@ math_2(PyObject *const *args, Py_ssize_t nargs, FUNC1(acos, acos, 0, "acos($module, x, /)\n--\n\n" "Return the arc cosine (measured in radians) of x.\n\n" - "The result is between 0 and pi.") + "The result is between 0 and pi.", + "math domain error") FUNC1(acosh, acosh, 0, "acosh($module, x, /)\n--\n\n" - "Return the inverse hyperbolic cosine of x.") + "Return the inverse hyperbolic cosine of x.", + "math domain error") FUNC1(asin, asin, 0, "asin($module, x, /)\n--\n\n" "Return the arc sine (measured in radians) of x.\n\n" - "The result is between -pi/2 and pi/2.") + "The result is between -pi/2 and pi/2.", + "math domain error") FUNC1(asinh, asinh, 0, "asinh($module, x, /)\n--\n\n" - "Return the inverse hyperbolic sine of x.") + "Return the inverse hyperbolic sine of x.", + "math domain error") FUNC1(atan, atan, 0, "atan($module, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of x.\n\n" - "The result is between -pi/2 and pi/2.") + "The result is between -pi/2 and pi/2.", + "math domain error") FUNC2(atan2, m_atan2, "atan2($module, y, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of y/x.\n\n" "Unlike atan(y/x), the signs of both x and y are considered.") FUNC1(atanh, atanh, 0, "atanh($module, x, /)\n--\n\n" - "Return the inverse hyperbolic tangent of x.") + "Return the inverse hyperbolic tangent of x.", + "math domain error") FUNC1(cbrt, cbrt, 0, "cbrt($module, x, /)\n--\n\n" - "Return the cube root of x.") + "Return the cube root of x.", + "math domain error") /*[clinic input] math.ceil @@ -1257,10 +1264,12 @@ FUNC2(copysign, copysign, "returns -1.0.\n") FUNC1(cos, cos, 0, "cos($module, x, /)\n--\n\n" - "Return the cosine of x (measured in radians).") + "Return the cosine of x (measured in radians).", + "math domain error") FUNC1(cosh, cosh, 1, "cosh($module, x, /)\n--\n\n" - "Return the hyperbolic cosine of x.") + "Return the hyperbolic cosine of x.", + "math domain error") FUNC1A(erf, m_erf, "erf($module, x, /)\n--\n\n" "Error function at x.") @@ -1269,18 +1278,22 @@ FUNC1A(erfc, m_erfc, "Complementary error function at x.") FUNC1(exp, exp, 1, "exp($module, x, /)\n--\n\n" - "Return e raised to the power of x.") + "Return e raised to the power of x.", + "math domain error") FUNC1(exp2, exp2, 1, "exp2($module, x, /)\n--\n\n" - "Return 2 raised to the power of x.") + "Return 2 raised to the power of x.", + "math domain error") FUNC1(expm1, expm1, 1, "expm1($module, x, /)\n--\n\n" "Return exp(x)-1.\n\n" "This function avoids the loss of precision involved in the direct " - "evaluation of exp(x)-1 for small x.") + "evaluation of exp(x)-1 for small x.", + "math domain error") FUNC1(fabs, fabs, 0, "fabs($module, x, /)\n--\n\n" - "Return the absolute value of the float x.") + "Return the absolute value of the float x.", + "math domain error") /*[clinic input] math.floor @@ -1329,7 +1342,8 @@ FUNC1A(lgamma, m_lgamma, FUNC1(log1p, m_log1p, 0, "log1p($module, x, /)\n--\n\n" "Return the natural logarithm of 1+x (base e).\n\n" - "The result is computed in a way which is accurate for x near zero.") + "The result is computed in a way which is accurate for x near zero.", + "math domain error") FUNC2(remainder, m_remainder, "remainder($module, x, y, /)\n--\n\n" "Difference between x and the closest integer multiple of y.\n\n" @@ -1338,19 +1352,25 @@ FUNC2(remainder, m_remainder, "y, the nearest even value of n is used. The result is always exact.") FUNC1(sin, sin, 0, "sin($module, x, /)\n--\n\n" - "Return the sine of x (measured in radians).") + "Return the sine of x (measured in radians).", + "math domain error") FUNC1(sinh, sinh, 1, "sinh($module, x, /)\n--\n\n" - "Return the hyperbolic sine of x.") + "Return the hyperbolic sine of x.", + "math domain error") FUNC1(sqrt, sqrt, 0, "sqrt($module, x, /)\n--\n\n" - "Return the square root of x.") + "Return the square root of x.", + "math.sqrt() expects a non-negative input, got '%R'.\n" + "See cmath.sqrt() for variation that supports complex numbers") FUNC1(tan, tan, 0, "tan($module, x, /)\n--\n\n" - "Return the tangent of x (measured in radians).") + "Return the tangent of x (measured in radians).", + "math domain error") FUNC1(tanh, tanh, 0, "tanh($module, x, /)\n--\n\n" - "Return the hyperbolic tangent of x.") + "Return the hyperbolic tangent of x.", + "math domain error") /* Precision summation function as msum() by Raymond Hettinger in , @@ -2358,7 +2378,7 @@ loghelper(PyObject* arg, double (*func)(double)) } /* Else let libm handle it by itself. */ - return math_1(arg, func, 0); + return math_1(arg, func, 0, "math domain error"); } From 2448fe4b24f747044c17c656c9c460e48c0c3bd6 Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Wed, 1 Feb 2023 14:42:49 +0800 Subject: [PATCH 2/8] Imporve error messages for `math.log()`. --- Modules/mathmodule.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 468fc1898270bb..c28aa9031fd4e6 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2344,7 +2344,7 @@ math_modf_impl(PyObject *module, double x) in that int is larger than PY_SSIZE_T_MAX. */ static PyObject* -loghelper(PyObject* arg, double (*func)(double)) +loghelper(PyObject* arg, double (*func)(double), const char* err_msg) { /* If it is int, do it ourselves. */ if (PyLong_Check(arg)) { @@ -2353,8 +2353,7 @@ loghelper(PyObject* arg, double (*func)(double)) /* Negative or zero inputs give a ValueError. */ if (Py_SIZE(arg) <= 0) { - PyErr_SetString(PyExc_ValueError, - "math domain error"); + PyErr_Format(PyExc_ValueError, err_msg, arg); return NULL; } @@ -2378,7 +2377,7 @@ loghelper(PyObject* arg, double (*func)(double)) } /* Else let libm handle it by itself. */ - return math_1(arg, func, 0, "math domain error"); + return math_1(arg, func, 0, err_msg); } @@ -2402,11 +2401,11 @@ math_log_impl(PyObject *module, PyObject *x, PyObject *base) PyObject *num, *den; PyObject *ans; - num = loghelper(x, m_log); + num = loghelper(x, m_log, "math.log() expects a positive input, got '%R'."); if (num == NULL || base == Py_None) return num; - den = loghelper(base, m_log); + den = loghelper(base, m_log, "math.log() expects a positive input, got '%R'."); if (den == NULL) { Py_DECREF(num); return NULL; @@ -2432,7 +2431,7 @@ static PyObject * math_log2(PyObject *module, PyObject *x) /*[clinic end generated code: output=5425899a4d5d6acb input=08321262bae4f39b]*/ { - return loghelper(x, m_log2); + return loghelper(x, m_log2, "math.log2() expects a positive input, got '%R'."); } @@ -2449,7 +2448,7 @@ static PyObject * math_log10(PyObject *module, PyObject *x) /*[clinic end generated code: output=be72a64617df9c6f input=b2469d02c6469e53]*/ { - return loghelper(x, m_log10); + return loghelper(x, m_log10, "math.log10() expects a positive input, got '%R'."); } From c0c1e2d4697ba1a0297a10fbf9aa3acd6a4023f7 Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Wed, 1 Feb 2023 16:43:08 +0800 Subject: [PATCH 3/8] Add a NEWS entry. --- .../next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst diff --git a/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst new file mode 100644 index 00000000000000..c479c2d5ffee99 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst @@ -0,0 +1,2 @@ +Make several math functions support custom error messages. ``math.sqrt()`` +and ``math.log()`` now provide more helpful error messages. \ No newline at end of file From 1a491f293bbdf4598e68fc5e4f2ed3169ee0f97a Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Wed, 1 Feb 2023 17:12:41 +0800 Subject: [PATCH 4/8] add new line --- .../next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst index c479c2d5ffee99..75b65f6c75d7eb 100644 --- a/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst +++ b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst @@ -1,2 +1,2 @@ Make several math functions support custom error messages. ``math.sqrt()`` -and ``math.log()`` now provide more helpful error messages. \ No newline at end of file +and ``math.log()`` now provide more helpful error messages. From 4cd75b01bf25882ae8feded08a0a1b4c30e66dd9 Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Mon, 6 Feb 2023 10:00:15 +0800 Subject: [PATCH 5/8] fix nit --- Modules/mathmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index c28aa9031fd4e6..af6cd189e1259d 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1361,8 +1361,8 @@ FUNC1(sinh, sinh, 1, FUNC1(sqrt, sqrt, 0, "sqrt($module, x, /)\n--\n\n" "Return the square root of x.", - "math.sqrt() expects a non-negative input, got '%R'.\n" - "See cmath.sqrt() for variation that supports complex numbers") + "math.sqrt() expects a nonnegative input, got '%R'.\n" + "See cmath.sqrt() for a variation that supports complex numbers") FUNC1(tan, tan, 0, "tan($module, x, /)\n--\n\n" "Return the tangent of x (measured in radians).", From 75a7c5ccf989de331b9a0035c287ff4fa6c1c9dc Mon Sep 17 00:00:00 2001 From: Charlie Zhao Date: Mon, 22 Jul 2024 15:43:04 +0800 Subject: [PATCH 6/8] Update Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst index 75b65f6c75d7eb..3619b2b9dd4b5b 100644 --- a/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst +++ b/Misc/NEWS.d/next/Library/2023-02-01-16-41-31.gh-issue-101410.Dt2aQE.rst @@ -1,2 +1,2 @@ -Make several math functions support custom error messages. ``math.sqrt()`` -and ``math.log()`` now provide more helpful error messages. +Make several math functions support custom error messages. :func:`math.sqrt` and :func:`math.log` +now provide more helpful error messages. From 4b2ec23d690e1d025cdc5fd849ba789c9a8ce27d Mon Sep 17 00:00:00 2001 From: Charlie Zhao Date: Mon, 22 Jul 2024 15:48:39 +0800 Subject: [PATCH 7/8] Update Modules/mathmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/mathmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 21ced7d5bb5927..553787549d5b01 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1261,7 +1261,7 @@ FUNC1(sqrt, sqrt, 0, "sqrt($module, x, /)\n--\n\n" "Return the square root of x.", "math.sqrt() expects a nonnegative input, got '%R'.\n" - "See cmath.sqrt() for a variation that supports complex numbers") + "See cmath.sqrt() for a variant that supports complex numbers") FUNC1(tan, tan, 0, "tan($module, x, /)\n--\n\n" "Return the tangent of x (measured in radians).", From 3fc73e9671ccb57bc324ec34e9aee405bc86aa57 Mon Sep 17 00:00:00 2001 From: Charlie Zhao Date: Mon, 22 Jul 2024 15:49:57 +0800 Subject: [PATCH 8/8] Update Modules/mathmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/mathmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 553787549d5b01..2bfbeb62d73940 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2236,7 +2236,7 @@ math_modf_impl(PyObject *module, double x) in that int is larger than PY_SSIZE_T_MAX. */ static PyObject* -loghelper(PyObject* arg, double (*func)(double), const char* err_msg) +loghelper(PyObject* arg, double (*func)(double), const char *err_msg) { /* If it is int, do it ourselves. */ if (PyLong_Check(arg)) {