Skip to content

Commit 2bfdc0a

Browse files
committed
gh-133895: provide C99 Annex F return values for math functions
Now this information is available as the "value" attribute of the ValueError exception object: ```pycon >>> import math >>> try: ... math.gamma(-0.0) ... except ValueError as exc: ... print(exc.value) ... -inf ```
1 parent f806463 commit 2bfdc0a

File tree

5 files changed

+39
-11
lines changed

5 files changed

+39
-11
lines changed

Doc/library/math.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,10 @@ Constants
822822
:exc:`ValueError` for invalid operations like ``sqrt(-1.0)`` or ``log(0.0)``
823823
(where C99 Annex F recommends signaling invalid operation or divide-by-zero),
824824
and :exc:`OverflowError` for results that overflow (for example,
825-
``exp(1000.0)``). A NaN will not be returned from any of the functions
825+
``exp(1000.0)``). The Annex F recommended result is available as
826+
the ``value`` property of the :exc:`ValueError` exception.
827+
828+
A NaN will not be returned from any of the functions
826829
above unless one or more of the input arguments was a NaN; in that case,
827830
most functions will return a NaN, but (again following C99 Annex F) there
828831
are some exceptions to this rule, for example ``pow(float('nan'), 0.0)`` or

Doc/whatsnew/3.15.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ difflib
105105
(Contributed by Jiahao Li in :gh:`134580`.)
106106

107107

108+
math
109+
----
110+
111+
* Provide C99 Annex F return values for :mod:`math`'s functions as the
112+
"value" attribute of the ValueError exception object.
113+
(Contributed by Sergey B Kirpichev in :gh:`133895`.)
114+
115+
108116
shelve
109117
------
110118

Lib/test/test_math.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,15 +2069,14 @@ def test_testfile(self):
20692069

20702070
func = getattr(math, fn)
20712071

2072-
if 'invalid' in flags or 'divide-by-zero' in flags:
2073-
er = 'ValueError'
2074-
elif 'overflow' in flags:
2072+
if 'overflow' in flags:
20752073
er = 'OverflowError'
20762074

20772075
try:
20782076
result = func(ar)
2079-
except ValueError:
2080-
result = 'ValueError'
2077+
except ValueError as exc:
2078+
self.assertTrue('invalid' in flags or 'divide-by-zero' in flags)
2079+
result = exc.value
20812080
except OverflowError:
20822081
result = 'OverflowError'
20832082

@@ -2110,15 +2109,14 @@ def test_mtestfile(self):
21102109
for id, fn, arg, expected, flags in parse_mtestfile(math_testcases):
21112110
func = getattr(math, fn)
21122111

2113-
if 'invalid' in flags or 'divide-by-zero' in flags:
2114-
expected = 'ValueError'
2115-
elif 'overflow' in flags:
2112+
if 'overflow' in flags:
21162113
expected = 'OverflowError'
21172114

21182115
try:
21192116
got = func(arg)
2120-
except ValueError:
2121-
got = 'ValueError'
2117+
except ValueError as exc:
2118+
got = exc.value
2119+
self.assertTrue('invalid' in flags or 'divide-by-zero' in flags)
21222120
except OverflowError:
21232121
got = 'OverflowError'
21242122

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Provide C99 Annex F return values for :mod:`math`'s functions as the "value"
2+
attribute of the ValueError exception object. Patch by Sergey B Kirpichev.

Modules/mathmodule.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,20 @@ math_lcm_impl(PyObject *module, PyObject * const *args,
840840
}
841841

842842

843+
static void
844+
set_exc_value(double x)
845+
{
846+
PyObject *exc = PyErr_GetRaisedException();
847+
PyObject *value = PyFloat_FromDouble(x);
848+
849+
if (value) {
850+
PyObject_SetAttrString(exc, "value", value);
851+
}
852+
Py_DECREF(value);
853+
PyErr_SetRaisedException(exc);
854+
}
855+
856+
843857
/* Call is_error when errno != 0, and where x is the result libm
844858
* returned. is_error will usually set up an exception and return
845859
* true (1), but may return false (0) without setting up an exception.
@@ -852,6 +866,7 @@ is_error(double x, int raise_edom)
852866
if (errno == EDOM) {
853867
if (raise_edom) {
854868
PyErr_SetString(PyExc_ValueError, "math domain error");
869+
set_exc_value(x);
855870
}
856871
}
857872

@@ -954,6 +969,7 @@ math_1(PyObject *arg, double (*func) (double), int can_overflow,
954969
else {
955970
PyErr_SetString(PyExc_ValueError, "math domain error");
956971
}
972+
set_exc_value(r);
957973
return NULL;
958974
}
959975

@@ -979,6 +995,7 @@ math_1a(PyObject *arg, double (*func) (double), const char *err_msg)
979995
PyMem_Free(buf);
980996
}
981997
}
998+
set_exc_value(r);
982999
return NULL;
9831000
}
9841001
return PyFloat_FromDouble(r);

0 commit comments

Comments
 (0)