gh-155526: Don't check errno in abs(complex) (#155527) · python/cpython@e5d4fa2 · GitHub
Skip to content

Commit e5d4fa2

Browse files
skirpichevvstinnerhpkfft
authored
gh-155526: Don't check errno in abs(complex) (#155527)
abs(complex) no longer raises OverflowError if errno was set to ERANGE by some library call but abs() doesn't overflow. _Py_c_abs() no longer sets errno to zero on success, but rather leaves it unchanged. Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: hpkfft.com <paul@hpkfft.com>
1 parent d85fa1a commit e5d4fa2

9 files changed

Lines changed: 76 additions & 19 deletions

File tree

Doc/c-api/complex.rst

Lines changed: 3 additions & 0 deletions

Doc/whatsnew/3.16.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,10 @@ Porting to Python 3.16
10181018
if the value cannot be marshalled.
10191019
(Contributed by Serhiy Storchaka in :gh:`155907`.)
10201020

1021+
* :c:func:`_Py_c_abs` no longer sets :c:data:`errno` to zero on success,
1022+
but rather leaves it unchanged.
1023+
(Contributed by Sergey B Kirpichev in :gh:`155526`.)
1024+
10211025
Deprecated C APIs
10221026
-----------------
10231027

Lib/test/test_capi/test_complex.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,18 +281,34 @@ def test_py_c_abs(self):
281281
# Test _Py_c_abs()
282282
_py_c_abs = _testcapi._py_c_abs
283283

284-
self.assertEqual(_py_c_abs(-1), (1.0, 0))
285-
self.assertEqual(_py_c_abs(1j), (1.0, 0))
286-
287-
self.assertEqual(_py_c_abs(complex('+inf+1j')), (INF, 0))
288-
self.assertEqual(_py_c_abs(complex('-inf+1j')), (INF, 0))
289-
self.assertEqual(_py_c_abs(complex('1.25+infj')), (INF, 0))
290-
self.assertEqual(_py_c_abs(complex('1.25-infj')), (INF, 0))
291-
292-
self.assertTrue(isnan(_py_c_abs(complex('1.25+nanj'))[0]))
293-
self.assertTrue(isnan(_py_c_abs(complex('nan-1j'))[0]))
294-
295-
self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE)
284+
def c_abs(num):
285+
# On success, _Py_c_abs() doesn't use errno and leaves errno
286+
# unchanged
287+
_testcapi.set_errno(0)
288+
result, errno = _py_c_abs(num)
289+
self.assertEqual(errno, 0)
290+
return result
291+
292+
try:
293+
self.assertEqual(c_abs(-1), 1.0)
294+
self.assertEqual(c_abs(1j), 1.0)
295+
self.assertEqual(c_abs(complex('+inf+1j')), INF)
296+
self.assertEqual(c_abs(complex('-inf+1j')), INF)
297+
self.assertEqual(c_abs(complex('1.25+infj')), INF)
298+
self.assertEqual(c_abs(complex('1.25-infj')), INF)
299+
self.assertTrue(isnan(c_abs(complex('1.25+nanj'))))
300+
self.assertTrue(isnan(c_abs(complex('nan-1j'))))
301+
302+
# Set errno to ERANGE on overflow
303+
_testcapi.set_errno(0)
304+
self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2)),
305+
(INF, errno.ERANGE))
306+
307+
# Preserve errno on success
308+
_testcapi.set_errno(errno.EACCES)
309+
self.assertEqual(_py_c_abs(1j), (1.0, errno.EACCES))
310+
finally:
311+
_testcapi.set_errno(0)
296312

297313

298314
if __name__ == "__main__":

Lib/test/test_complex.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import errno
12
import unittest
23
import sys
34
from test import support
5+
from test.support import import_helper
46
from test.support.testcase import ComplexesAreIdenticalMixin
57
from test.support.numbers import (
68
VALID_UNDERSCORE_LITERALS,
@@ -9,6 +11,7 @@
911

1012
from random import random
1113
from math import isnan, copysign
14+
import cmath
1215
import operator
1316

1417
INF = float("inf")
@@ -860,8 +863,30 @@ def test_abs(self):
860863
for num in nums:
861864
self.assertAlmostEqual((num.real**2 + num.imag**2) ** 0.5, abs(num))
862865

866+
for x in 0.0, -0.0, INF, -INF, NAN:
867+
for y in 0.0, -0.0, INF, -INF, NAN:
868+
with self.subTest(x=x, y=y):
869+
z = complex(x, y)
870+
r = abs(z)
871+
if cmath.isfinite(z):
872+
self.assertFloatsAreIdentical(r, 0.0)
873+
elif cmath.isinf(z):
874+
self.assertEqual(r, INF)
875+
else:
876+
self.assertTrue(cmath.isnan(z))
877+
self.assertTrue(isnan(r))
878+
863879
self.assertRaises(OverflowError, abs, complex(DBL_MAX, DBL_MAX))
864880

881+
def test_abs_errno_handling(self):
882+
_testcapi = import_helper.import_module('_testcapi')
883+
z = complex('nan')
884+
_testcapi.set_errno(errno.ERANGE)
885+
try:
886+
self.assertTrue(isnan(abs(z)))
887+
finally:
888+
_testcapi.set_errno(0)
889+
865890
def test_repr_str(self):
866891
def test(v, expected, test_fn=self.assertEqual):
867892
test_fn(repr(v), expected)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:c:func:`_Py_c_abs` no longer sets :c:data:`errno` to zero on success, but
2+
rather leaves it unchanged. Patch by Sergey B Kirpichev.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix spurious :exc:`OverflowError` for ``abs(nanj)`` in case :c:data:`errno` was
2+
previously set to :c:macro:`!ERANGE` by some library call.
3+
Patch by Sergey B Kirpichev.

Modules/_testcapi/complex.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ _py_c_abs(PyObject *Py_UNUSED(module), PyObject* obj)
7676
return NULL;
7777
}
7878

79-
errno = 0;
8079
res = _Py_c_abs(complex);
8180
return Py_BuildValue("di", res, errno);
8281
}

Modules/cmathmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,8 +1029,8 @@ cmath_polar_impl(PyObject *module, Py_complex z)
10291029
{
10301030
double r, phi;
10311031

1032-
errno = 0;
10331032
phi = atan2(z.imag, z.real); /* should not cause any exception */
1033+
errno = 0;
10341034
r = _Py_c_abs(z); /* sets errno to ERANGE on overflow */
10351035
if (errno != 0)
10361036
return math_error();

Objects/complexobject.c

Lines changed: 10 additions & 5 deletions

0 commit comments

Comments
 (0)