Skip to content

gh-101410: customize error messages for 1-arg math functions #129497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Lib/test/mathdata/ieee754.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,31 +127,31 @@ Trigonometric Functions
>>> sin(INF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got inf
>>> sin(NINF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got -inf
>>> sin(NAN)
nan
>>> cos(INF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got inf
>>> cos(NINF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got -inf
>>> cos(NAN)
nan
>>> tan(INF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got inf
>>> tan(NINF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a finite input, got -inf
>>> tan(NAN)
nan

Expand All @@ -169,11 +169,11 @@ True
>>> asin(INF), asin(NINF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a number in range from -1 up to 1, got inf
>>> acos(INF), acos(NINF)
Traceback (most recent call last):
...
ValueError: math domain error
ValueError: expected a number in range from -1 up to 1, got inf
>>> equal(atan(INF), PI/2), equal(atan(NINF), -PI/2)
(True, True)

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -2533,7 +2533,7 @@ def test_exception_messages(self):
math.log(x)
x = -123
with self.assertRaisesRegex(ValueError,
f"expected a positive input, got {x}"):
f"expected a positive input"):
math.log(x)
with self.assertRaisesRegex(ValueError,
f"expected a float or nonnegative integer, got {x}"):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
Support custom messages for domain errors in the :mod:`math` module
(:func:`math.sqrt`, :func:`math.log` and :func:`math.atanh` were modified as
examples). Patch by Charlie Zhao and Sergey B Kirpichev.
Support custom messages for domain errors in the :mod:`math` module.
Patch by Charlie Zhao and Sergey B Kirpichev.
46 changes: 28 additions & 18 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1089,17 +1089,20 @@ math_2(PyObject *const *args, Py_ssize_t nargs,
}\
PyDoc_STRVAR(math_##funcname##_doc, docstring);

FUNC1(acos, acos, 0,
FUNC1D(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.")
FUNC1(acosh, acosh, 0,
"The result is between 0 and pi.",
"expected a number in range from -1 up to 1, got %s")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could say "a number in the interval [-1, 1]" though I'm a bit worried about the notation that may not be understood (for non mathematicians). Otherwise shouldn't we say "in the range from -1 up to 1" ? (I'm not sure here for the English part so @AA-Turner could help us)

FUNC1D(acosh, acosh, 0,
"acosh($module, x, /)\n--\n\n"
"Return the inverse hyperbolic cosine of x.")
FUNC1(asin, asin, 0,
"Return the inverse hyperbolic cosine of x.",
"expected argument value not less than 1, got %s")
FUNC1D(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.",
"expected a number in range from -1 up to 1, got %s")
FUNC1(asinh, asinh, 0,
"asinh($module, x, /)\n--\n\n"
"Return the inverse hyperbolic sine of x.")
Expand Down Expand Up @@ -1161,9 +1164,10 @@ FUNC2(copysign, copysign,
"Return a float with the magnitude (absolute value) of x but the sign of y.\n\n"
"On platforms that support signed zeros, copysign(1.0, -0.0)\n"
"returns -1.0.\n")
FUNC1(cos, cos, 0,
FUNC1D(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).",
"expected a finite input, got %s")
FUNC1(cosh, cosh, 1,
"cosh($module, x, /)\n--\n\n"
"Return the hyperbolic cosine of x.")
Expand Down Expand Up @@ -1229,32 +1233,36 @@ FUNC1AD(gamma, m_tgamma,
"gamma($module, x, /)\n--\n\n"
"Gamma function at x.",
"expected a float or nonnegative integer, got %s")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get error for 0, which is a non-negative integer:

>>> math.gamma(0)
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    math.gamma(0)
    ~~~~~~~~~~^^^
ValueError: expected a float or nonnegative integer, got 0.0

I get also error for negative float value which happens to be an integer. The error message is not clear about this.

>>> math.gamma(-1.0)
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    math.gamma(-1.0)
    ~~~~~~~~~~^^^^^^
ValueError: expected a float or nonnegative integer, got -1.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

math.gamma(0)

Thanks, indeed - this should be positive, as for lgamma().

I get also error for negative float value which happens to be an integer. The error message is not clear about this.

I don't see a problem here: the message says we expect nonnegative (>=0) input here (actually, it should be just positive) and you pass a negative integer float value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It says "or". -1.0 is a float. "Nonnegative" only applies to "integer".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1.0 is an integer in sense of is_integer() predicate or ==.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But is is also a float. "a or b" is true if "a" is true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"expected a noninteger or positive integer, got %s"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this, or via the complement of the function domain. It raises ValueError only for non-positive integers, isn't?

OverflowError with generic error message is raised for other values.

FUNC1A(lgamma, m_lgamma,
FUNC1AD(lgamma, m_lgamma,
"lgamma($module, x, /)\n--\n\n"
"Natural logarithm of absolute value of Gamma function at x.")
FUNC1(log1p, m_log1p, 0,
"Natural logarithm of absolute value of Gamma function at x.",
"expected a float or nonnegative integer, got %s")
FUNC1D(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.",
"expected argument value > -1, got %s")
FUNC2(remainder, m_remainder,
"remainder($module, x, y, /)\n--\n\n"
"Difference between x and the closest integer multiple of y.\n\n"
"Return x - n*y where n*y is the closest integer multiple of y.\n"
"In the case where x is exactly halfway between two multiples of\n"
"y, the nearest even value of n is used. The result is always exact.")
FUNC1(sin, sin, 0,
FUNC1D(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).",
"expected a finite input, got %s")
FUNC1(sinh, sinh, 1,
"sinh($module, x, /)\n--\n\n"
"Return the hyperbolic sine of x.")
FUNC1D(sqrt, sqrt, 0,
"sqrt($module, x, /)\n--\n\n"
"Return the square root of x.",
"expected a nonnegative input, got %s")
FUNC1(tan, tan, 0,
FUNC1D(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).",
"expected a finite input, got %s")
FUNC1(tanh, tanh, 0,
"tanh($module, x, /)\n--\n\n"
"Return the hyperbolic tangent of x.")
Expand Down Expand Up @@ -2232,8 +2240,10 @@ loghelper(PyObject* arg, double (*func)(double))

/* Negative or zero inputs give a ValueError. */
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
PyErr_Format(PyExc_ValueError,
"expected a positive input, got %S", arg);
/* The input can be an arbitrary large integer, so we
don't include it's value in the error message. */
PyErr_SetString(PyExc_ValueError,
"expected a positive input");
return NULL;
}

Expand Down
Loading