From b88f6570eeb135f58231da6d6937d5ea1535609d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Nov 2023 08:11:12 +0300 Subject: [PATCH 01/15] gh-111495: Add tests for PyComplex C API --- Lib/test/test_complex.py | 126 ++++++++++++++++++++++++++++++++++++ Modules/_testcapi/complex.c | 97 +++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 9180cca62b28b8..4638c32da10e00 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -3,11 +3,17 @@ from test import support from test.test_grammar import (VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS) +from test.support import import_helper +from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex, + FloatSubclass, Float, BadFloat, + BadFloat2, ComplexSubclass) from random import random from math import atan2, isnan, copysign import operator +_testcapi = import_helper.import_module('_testcapi') + INF = float("inf") NAN = float("nan") # These tests ensure that complex math does the right thing @@ -20,6 +26,8 @@ (1, 0+0j), ) +NULL = None + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -72,6 +80,15 @@ def assertFloatsAreIdentical(self, x, y): msg += ': zeros have different signs' self.fail(msg.format(x, y)) + def assertComplexesAreIdentical(self, x, y): + """assert that complex numbers x and y are identical + + I.e. they have identical real and imag components. + + """ + (self.assertFloatsAreIdentical(x.real, y.real) + and self.assertFloatsAreIdentical(x.imag, y.imag)) + def assertClose(self, x, y, eps=1e-9): """Return true iff complexes x and y "are close".""" self.assertCloseAbs(x.real, y.real, eps) @@ -792,5 +809,114 @@ def test_format(self): self.assertEqual(format(complex(INF, -1), 'F'), 'INF-1.000000j') +class CAPIComplexTest(ComplexTest): + def test_check(self): + # Test PyComplex_Check() + check = _testcapi.complex_check + + self.assertTrue(check(1+2j)) + self.assertTrue(check(ComplexSubclass(1+2j))) + self.assertFalse(check(Complex())) + self.assertFalse(check(3)) + self.assertFalse(check([])) + self.assertFalse(check(object())) + + # CRASHES check(NULL) + + def test_checkexact(self): + # PyComplex_CheckExact() + checkexact = _testcapi.complex_checkexact + + self.assertTrue(checkexact(1+2j)) + self.assertFalse(checkexact(ComplexSubclass(1+2j))) + self.assertFalse(checkexact(Complex())) + self.assertFalse(checkexact(3)) + self.assertFalse(checkexact([])) + self.assertFalse(checkexact(object())) + + # CRASHES checkexact(NULL) + + def test_fromccomplex(self): + # Test PyComplex_FromCComplex() + fromccomplex = _testcapi.complex_fromccomplex + + self.assertComplexesAreIdentical(fromccomplex(1+2j), 1.0+2.0j) + + def test_fromdoubles(self): + # Test PyComplex_FromDoubles() + fromdoubles = _testcapi.complex_fromdoubles + + self.assertComplexesAreIdentical(fromdoubles(1.0, 2.0), 1.0+2.0j) + + def test_realasdouble(self): + # Test PyComplex_RealAsDouble() + realasdouble = _testcapi.complex_realasdouble + + self.assertFloatsAreIdentical(realasdouble(1+2j), 1.0) + self.assertFloatsAreIdentical(realasdouble(1), 1.0) + self.assertFloatsAreIdentical(realasdouble(-1), -1.0) + # Function doesn't support classes with __complex__ dunder, see #109598 + #self.assertFloatsAreIdentical(realasdouble(Complex()), 4.25) + #self.assertFloatsAreIdentical(realasdouble(3.14), 3.14) + #self.assertFloatsAreIdentical(realasdouble(FloatSubclass(3.14)), 3.14) + #self.assertFloatsAreIdentical(realasdouble(Float()), 4.25) + #with self.assertWarns(DeprecationWarning): + # self.assertFloatsAreIdentical(realasdouble(BadComplex2()), 4.25) + #with self.assertWarns(DeprecationWarning): + # self.assertFloatsAreIdentical(realasdouble(BadFloat2()), 4.25) + self.assertRaises(TypeError, realasdouble, BadComplex()) + self.assertRaises(TypeError, realasdouble, BadFloat()) + self.assertRaises(TypeError, realasdouble, object()) + + # CRASHES realasdouble(NULL) + + def test_imagasdouble(self): + # Test PyComplex_ImagAsDouble() + imagasdouble = _testcapi.complex_imagasdouble + + self.assertFloatsAreIdentical(imagasdouble(1+2j), 2.0) + self.assertFloatsAreIdentical(imagasdouble(1), 0.0) + self.assertFloatsAreIdentical(imagasdouble(-1), 0.0) + # Function doesn't support classes with __complex__ dunder, see #109598 + #self.assertFloatsAreIdentical(imagasdouble(Complex()), 0.5) + #self.assertFloatsAreIdentical(imagasdouble(3.14), 0.0) + #self.assertFloatsAreIdentical(imagasdouble(FloatSubclass(3.14)), 0.0) + #self.assertFloatsAreIdentical(imagasdouble(Float()), 0.0) + #with self.assertWarns(DeprecationWarning): + # self.assertFloatsAreIdentical(imagasdouble(BadComplex2()), 0.5) + #with self.assertWarns(DeprecationWarning): + # self.assertFloatsAreIdentical(imagasdouble(BadFloat2()), 0.0) + # Function returns 0.0 anyway, see #109598 + #self.assertRaises(TypeError, imagasdouble, BadComplex()) + #self.assertRaises(TypeError, imagasdouble, BadFloat()) + #self.assertRaises(TypeError, imagasdouble, object()) + self.assertFloatsAreIdentical(imagasdouble(BadComplex()), 0.0) + self.assertFloatsAreIdentical(imagasdouble(BadFloat()), 0.0) + self.assertFloatsAreIdentical(imagasdouble(object()), 0.0) + + # CRASHES imagasdouble(NULL) + + def test_asccomplex(self): + # Test PyComplex_AsCComplex() + asccomplex = _testcapi.complex_asccomplex + + self.assertComplexesAreIdentical(asccomplex(1+2j), 1.0+2.0j) + self.assertComplexesAreIdentical(asccomplex(1), 1.0+0.0j) + self.assertComplexesAreIdentical(asccomplex(-1), -1.0+0.0j) + self.assertComplexesAreIdentical(asccomplex(Complex()), 4.25+0.5j) + self.assertComplexesAreIdentical(asccomplex(3.14), 3.14+0.0j) + self.assertComplexesAreIdentical(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) + self.assertComplexesAreIdentical(asccomplex(Float()), 4.25+0.0j) + with self.assertWarns(DeprecationWarning): + self.assertComplexesAreIdentical(asccomplex(BadComplex2()), 4.25+0.5j) + with self.assertWarns(DeprecationWarning): + self.assertComplexesAreIdentical(asccomplex(BadFloat2()), 4.25+0.0j) + self.assertRaises(TypeError, asccomplex, BadComplex()) + self.assertRaises(TypeError, asccomplex, BadFloat()) + self.assertRaises(TypeError, asccomplex, object()) + + # CRASHES asccomplex(NULL) + + if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index 0402b8ecc9588f..cca3a9ef85c381 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -1,7 +1,104 @@ #include "parts.h" #include "util.h" + +static PyObject * +complex_check(PyObject *Py_UNUSED(module), PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyComplex_Check(obj)); +} + +static PyObject * +complex_checkexact(PyObject *Py_UNUSED(module), PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyComplex_CheckExact(obj)); +} + +static PyObject * +complex_fromccomplex(PyObject *Py_UNUSED(module), PyObject *obj) +{ + Py_complex complex = ((PyComplexObject*)obj)->cval; + + return PyComplex_FromCComplex(complex); +} + +static PyObject * +complex_fromdoubles(PyObject *Py_UNUSED(module), PyObject *const *args, + Py_ssize_t nargs) +{ + double real, imag; + + assert(nargs == 2); + + real = PyFloat_AsDouble(args[0]); + if (real == -1. && PyErr_Occurred()) { + return NULL; + } + + imag = PyFloat_AsDouble(args[1]); + if (imag == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromDoubles(real, imag); +} + +static PyObject * +complex_realasdouble(PyObject *Py_UNUSED(module), PyObject *obj) +{ + double real; + + NULLABLE(obj); + real = PyComplex_RealAsDouble(obj); + + if (real == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyFloat_FromDouble(real); +} + +static PyObject * +complex_imagasdouble(PyObject *Py_UNUSED(module), PyObject *obj) +{ + double imag; + + NULLABLE(obj); + imag = PyComplex_ImagAsDouble(obj); + + if (imag == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyFloat_FromDouble(imag); +} + +static PyObject * +complex_asccomplex(PyObject *Py_UNUSED(module), PyObject *obj) +{ + Py_complex complex; + + NULLABLE(obj); + complex = PyComplex_AsCComplex(obj); + + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromCComplex(complex); +} + + static PyMethodDef test_methods[] = { + {"complex_check", complex_check, METH_O}, + {"complex_checkexact", complex_checkexact, METH_O}, + {"complex_fromccomplex", complex_fromccomplex, METH_O}, + {"complex_fromdoubles", _PyCFunction_CAST(complex_fromdoubles), METH_FASTCALL}, + {"complex_realasdouble", complex_realasdouble, METH_O}, + {"complex_imagasdouble", complex_imagasdouble, METH_O}, + {"complex_asccomplex", complex_asccomplex, METH_O}, {NULL}, }; From 9f04bcb6c8213aadbfbdd1cfe7dc88f6d01bf06a Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Nov 2023 16:24:03 +0300 Subject: [PATCH 02/15] Simplify complex_fromdoubles() --- Modules/_testcapi/complex.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index cca3a9ef85c381..e4e76bbd5ec8d7 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -25,20 +25,11 @@ complex_fromccomplex(PyObject *Py_UNUSED(module), PyObject *obj) } static PyObject * -complex_fromdoubles(PyObject *Py_UNUSED(module), PyObject *const *args, - Py_ssize_t nargs) +complex_fromdoubles(PyObject *Py_UNUSED(module), PyObject *args) { double real, imag; - assert(nargs == 2); - - real = PyFloat_AsDouble(args[0]); - if (real == -1. && PyErr_Occurred()) { - return NULL; - } - - imag = PyFloat_AsDouble(args[1]); - if (imag == -1. && PyErr_Occurred()) { + if (!PyArg_ParseTuple(args, "dd", &real, &imag)) { return NULL; } @@ -95,7 +86,7 @@ static PyMethodDef test_methods[] = { {"complex_check", complex_check, METH_O}, {"complex_checkexact", complex_checkexact, METH_O}, {"complex_fromccomplex", complex_fromccomplex, METH_O}, - {"complex_fromdoubles", _PyCFunction_CAST(complex_fromdoubles), METH_FASTCALL}, + {"complex_fromdoubles", complex_fromdoubles, METH_VARARGS}, {"complex_realasdouble", complex_realasdouble, METH_O}, {"complex_imagasdouble", complex_imagasdouble, METH_O}, {"complex_asccomplex", complex_asccomplex, METH_O}, From d19a9d433fc818bd0487d5054e0a9a386791a708 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Nov 2023 18:30:38 +0300 Subject: [PATCH 03/15] Use assertEqual() --- Lib/test/test_complex.py | 73 ++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 4638c32da10e00..d360c307cc9df9 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -80,15 +80,6 @@ def assertFloatsAreIdentical(self, x, y): msg += ': zeros have different signs' self.fail(msg.format(x, y)) - def assertComplexesAreIdentical(self, x, y): - """assert that complex numbers x and y are identical - - I.e. they have identical real and imag components. - - """ - (self.assertFloatsAreIdentical(x.real, y.real) - and self.assertFloatsAreIdentical(x.imag, y.imag)) - def assertClose(self, x, y, eps=1e-9): """Return true iff complexes x and y "are close".""" self.assertCloseAbs(x.real, y.real, eps) @@ -840,30 +831,30 @@ def test_fromccomplex(self): # Test PyComplex_FromCComplex() fromccomplex = _testcapi.complex_fromccomplex - self.assertComplexesAreIdentical(fromccomplex(1+2j), 1.0+2.0j) + self.assertEqual(fromccomplex(1+2j), 1.0+2.0j) def test_fromdoubles(self): # Test PyComplex_FromDoubles() fromdoubles = _testcapi.complex_fromdoubles - self.assertComplexesAreIdentical(fromdoubles(1.0, 2.0), 1.0+2.0j) + self.assertEqual(fromdoubles(1.0, 2.0), 1.0+2.0j) def test_realasdouble(self): # Test PyComplex_RealAsDouble() realasdouble = _testcapi.complex_realasdouble - self.assertFloatsAreIdentical(realasdouble(1+2j), 1.0) - self.assertFloatsAreIdentical(realasdouble(1), 1.0) - self.assertFloatsAreIdentical(realasdouble(-1), -1.0) + self.assertEqual(realasdouble(1+2j), 1.0) + self.assertEqual(realasdouble(1), 1.0) + self.assertEqual(realasdouble(-1), -1.0) # Function doesn't support classes with __complex__ dunder, see #109598 - #self.assertFloatsAreIdentical(realasdouble(Complex()), 4.25) - #self.assertFloatsAreIdentical(realasdouble(3.14), 3.14) - #self.assertFloatsAreIdentical(realasdouble(FloatSubclass(3.14)), 3.14) - #self.assertFloatsAreIdentical(realasdouble(Float()), 4.25) + #self.assertEqual(realasdouble(Complex()), 4.25) + #self.assertEqual(realasdouble(3.14), 3.14) + #self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) + #self.assertEqual(realasdouble(Float()), 4.25) #with self.assertWarns(DeprecationWarning): - # self.assertFloatsAreIdentical(realasdouble(BadComplex2()), 4.25) + # self.assertEqual(realasdouble(BadComplex2()), 4.25) #with self.assertWarns(DeprecationWarning): - # self.assertFloatsAreIdentical(realasdouble(BadFloat2()), 4.25) + # self.assertEqual(realasdouble(BadFloat2()), 4.25) self.assertRaises(TypeError, realasdouble, BadComplex()) self.assertRaises(TypeError, realasdouble, BadFloat()) self.assertRaises(TypeError, realasdouble, object()) @@ -874,25 +865,25 @@ def test_imagasdouble(self): # Test PyComplex_ImagAsDouble() imagasdouble = _testcapi.complex_imagasdouble - self.assertFloatsAreIdentical(imagasdouble(1+2j), 2.0) - self.assertFloatsAreIdentical(imagasdouble(1), 0.0) - self.assertFloatsAreIdentical(imagasdouble(-1), 0.0) + self.assertEqual(imagasdouble(1+2j), 2.0) + self.assertEqual(imagasdouble(1), 0.0) + self.assertEqual(imagasdouble(-1), 0.0) # Function doesn't support classes with __complex__ dunder, see #109598 - #self.assertFloatsAreIdentical(imagasdouble(Complex()), 0.5) - #self.assertFloatsAreIdentical(imagasdouble(3.14), 0.0) - #self.assertFloatsAreIdentical(imagasdouble(FloatSubclass(3.14)), 0.0) - #self.assertFloatsAreIdentical(imagasdouble(Float()), 0.0) + #self.assertEqual(imagasdouble(Complex()), 0.5) + #self.assertEqual(imagasdouble(3.14), 0.0) + #self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) + #self.assertEqual(imagasdouble(Float()), 0.0) #with self.assertWarns(DeprecationWarning): - # self.assertFloatsAreIdentical(imagasdouble(BadComplex2()), 0.5) + # self.assertEqual(imagasdouble(BadComplex2()), 0.5) #with self.assertWarns(DeprecationWarning): - # self.assertFloatsAreIdentical(imagasdouble(BadFloat2()), 0.0) + # self.assertEqual(imagasdouble(BadFloat2()), 0.0) # Function returns 0.0 anyway, see #109598 #self.assertRaises(TypeError, imagasdouble, BadComplex()) #self.assertRaises(TypeError, imagasdouble, BadFloat()) #self.assertRaises(TypeError, imagasdouble, object()) - self.assertFloatsAreIdentical(imagasdouble(BadComplex()), 0.0) - self.assertFloatsAreIdentical(imagasdouble(BadFloat()), 0.0) - self.assertFloatsAreIdentical(imagasdouble(object()), 0.0) + self.assertEqual(imagasdouble(BadComplex()), 0.0) + self.assertEqual(imagasdouble(BadFloat()), 0.0) + self.assertEqual(imagasdouble(object()), 0.0) # CRASHES imagasdouble(NULL) @@ -900,17 +891,17 @@ def test_asccomplex(self): # Test PyComplex_AsCComplex() asccomplex = _testcapi.complex_asccomplex - self.assertComplexesAreIdentical(asccomplex(1+2j), 1.0+2.0j) - self.assertComplexesAreIdentical(asccomplex(1), 1.0+0.0j) - self.assertComplexesAreIdentical(asccomplex(-1), -1.0+0.0j) - self.assertComplexesAreIdentical(asccomplex(Complex()), 4.25+0.5j) - self.assertComplexesAreIdentical(asccomplex(3.14), 3.14+0.0j) - self.assertComplexesAreIdentical(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) - self.assertComplexesAreIdentical(asccomplex(Float()), 4.25+0.0j) + self.assertEqual(asccomplex(1+2j), 1.0+2.0j) + self.assertEqual(asccomplex(1), 1.0+0.0j) + self.assertEqual(asccomplex(-1), -1.0+0.0j) + self.assertEqual(asccomplex(Complex()), 4.25+0.5j) + self.assertEqual(asccomplex(3.14), 3.14+0.0j) + self.assertEqual(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) + self.assertEqual(asccomplex(Float()), 4.25+0.0j) with self.assertWarns(DeprecationWarning): - self.assertComplexesAreIdentical(asccomplex(BadComplex2()), 4.25+0.5j) + self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) with self.assertWarns(DeprecationWarning): - self.assertComplexesAreIdentical(asccomplex(BadFloat2()), 4.25+0.0j) + self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, BadComplex()) self.assertRaises(TypeError, asccomplex, BadFloat()) self.assertRaises(TypeError, asccomplex, object()) From c8adbd2c0ca23e82c6dbd8013bce3c7f151f75a2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Nov 2023 18:35:18 +0300 Subject: [PATCH 04/15] Move tests to Lib/test/test_capi/test_complex.py --- Lib/test/test_capi/test_complex.py | 124 +++++++++++++++++++++++++++++ Lib/test/test_complex.py | 117 --------------------------- 2 files changed, 124 insertions(+), 117 deletions(-) create mode 100644 Lib/test/test_capi/test_complex.py diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py new file mode 100644 index 00000000000000..2d913604cc03a4 --- /dev/null +++ b/Lib/test/test_capi/test_complex.py @@ -0,0 +1,124 @@ +import unittest + +from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex, + FloatSubclass, Float, BadFloat, + BadFloat2, ComplexSubclass) +from test.support import import_helper + + +_testcapi = import_helper.import_module('_testcapi') + +NULL = None + + +class CAPIComplexTest(unittest.TestCase): + def test_check(self): + # Test PyComplex_Check() + check = _testcapi.complex_check + + self.assertTrue(check(1+2j)) + self.assertTrue(check(ComplexSubclass(1+2j))) + self.assertFalse(check(Complex())) + self.assertFalse(check(3)) + self.assertFalse(check([])) + self.assertFalse(check(object())) + + # CRASHES check(NULL) + + def test_checkexact(self): + # PyComplex_CheckExact() + checkexact = _testcapi.complex_checkexact + + self.assertTrue(checkexact(1+2j)) + self.assertFalse(checkexact(ComplexSubclass(1+2j))) + self.assertFalse(checkexact(Complex())) + self.assertFalse(checkexact(3)) + self.assertFalse(checkexact([])) + self.assertFalse(checkexact(object())) + + # CRASHES checkexact(NULL) + + def test_fromccomplex(self): + # Test PyComplex_FromCComplex() + fromccomplex = _testcapi.complex_fromccomplex + + self.assertEqual(fromccomplex(1+2j), 1.0+2.0j) + + def test_fromdoubles(self): + # Test PyComplex_FromDoubles() + fromdoubles = _testcapi.complex_fromdoubles + + self.assertEqual(fromdoubles(1.0, 2.0), 1.0+2.0j) + + def test_realasdouble(self): + # Test PyComplex_RealAsDouble() + realasdouble = _testcapi.complex_realasdouble + + self.assertEqual(realasdouble(1+2j), 1.0) + self.assertEqual(realasdouble(1), 1.0) + self.assertEqual(realasdouble(-1), -1.0) + # Function doesn't support classes with __complex__ dunder, see #109598 + #self.assertEqual(realasdouble(Complex()), 4.25) + #self.assertEqual(realasdouble(3.14), 3.14) + #self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) + #self.assertEqual(realasdouble(Float()), 4.25) + #with self.assertWarns(DeprecationWarning): + # self.assertEqual(realasdouble(BadComplex2()), 4.25) + #with self.assertWarns(DeprecationWarning): + # self.assertEqual(realasdouble(BadFloat2()), 4.25) + self.assertRaises(TypeError, realasdouble, BadComplex()) + self.assertRaises(TypeError, realasdouble, BadFloat()) + self.assertRaises(TypeError, realasdouble, object()) + + # CRASHES realasdouble(NULL) + + def test_imagasdouble(self): + # Test PyComplex_ImagAsDouble() + imagasdouble = _testcapi.complex_imagasdouble + + self.assertEqual(imagasdouble(1+2j), 2.0) + self.assertEqual(imagasdouble(1), 0.0) + self.assertEqual(imagasdouble(-1), 0.0) + # Function doesn't support classes with __complex__ dunder, see #109598 + #self.assertEqual(imagasdouble(Complex()), 0.5) + #self.assertEqual(imagasdouble(3.14), 0.0) + #self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) + #self.assertEqual(imagasdouble(Float()), 0.0) + #with self.assertWarns(DeprecationWarning): + # self.assertEqual(imagasdouble(BadComplex2()), 0.5) + #with self.assertWarns(DeprecationWarning): + # self.assertEqual(imagasdouble(BadFloat2()), 0.0) + # Function returns 0.0 anyway, see #109598 + #self.assertRaises(TypeError, imagasdouble, BadComplex()) + #self.assertRaises(TypeError, imagasdouble, BadFloat()) + #self.assertRaises(TypeError, imagasdouble, object()) + self.assertEqual(imagasdouble(BadComplex()), 0.0) + self.assertEqual(imagasdouble(BadFloat()), 0.0) + self.assertEqual(imagasdouble(object()), 0.0) + + # CRASHES imagasdouble(NULL) + + def test_asccomplex(self): + # Test PyComplex_AsCComplex() + asccomplex = _testcapi.complex_asccomplex + + self.assertEqual(asccomplex(1+2j), 1.0+2.0j) + self.assertEqual(asccomplex(1), 1.0+0.0j) + self.assertEqual(asccomplex(-1), -1.0+0.0j) + self.assertEqual(asccomplex(Complex()), 4.25+0.5j) + self.assertEqual(asccomplex(3.14), 3.14+0.0j) + self.assertEqual(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) + self.assertEqual(asccomplex(Float()), 4.25+0.0j) + with self.assertWarns(DeprecationWarning): + self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) + with self.assertWarns(DeprecationWarning): + self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) + self.assertRaises(TypeError, asccomplex, BadComplex()) + self.assertRaises(TypeError, asccomplex, BadFloat()) + self.assertRaises(TypeError, asccomplex, object()) + + # CRASHES asccomplex(NULL) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index d360c307cc9df9..9180cca62b28b8 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -3,17 +3,11 @@ from test import support from test.test_grammar import (VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS) -from test.support import import_helper -from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex, - FloatSubclass, Float, BadFloat, - BadFloat2, ComplexSubclass) from random import random from math import atan2, isnan, copysign import operator -_testcapi = import_helper.import_module('_testcapi') - INF = float("inf") NAN = float("nan") # These tests ensure that complex math does the right thing @@ -26,8 +20,6 @@ (1, 0+0j), ) -NULL = None - class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -800,114 +792,5 @@ def test_format(self): self.assertEqual(format(complex(INF, -1), 'F'), 'INF-1.000000j') -class CAPIComplexTest(ComplexTest): - def test_check(self): - # Test PyComplex_Check() - check = _testcapi.complex_check - - self.assertTrue(check(1+2j)) - self.assertTrue(check(ComplexSubclass(1+2j))) - self.assertFalse(check(Complex())) - self.assertFalse(check(3)) - self.assertFalse(check([])) - self.assertFalse(check(object())) - - # CRASHES check(NULL) - - def test_checkexact(self): - # PyComplex_CheckExact() - checkexact = _testcapi.complex_checkexact - - self.assertTrue(checkexact(1+2j)) - self.assertFalse(checkexact(ComplexSubclass(1+2j))) - self.assertFalse(checkexact(Complex())) - self.assertFalse(checkexact(3)) - self.assertFalse(checkexact([])) - self.assertFalse(checkexact(object())) - - # CRASHES checkexact(NULL) - - def test_fromccomplex(self): - # Test PyComplex_FromCComplex() - fromccomplex = _testcapi.complex_fromccomplex - - self.assertEqual(fromccomplex(1+2j), 1.0+2.0j) - - def test_fromdoubles(self): - # Test PyComplex_FromDoubles() - fromdoubles = _testcapi.complex_fromdoubles - - self.assertEqual(fromdoubles(1.0, 2.0), 1.0+2.0j) - - def test_realasdouble(self): - # Test PyComplex_RealAsDouble() - realasdouble = _testcapi.complex_realasdouble - - self.assertEqual(realasdouble(1+2j), 1.0) - self.assertEqual(realasdouble(1), 1.0) - self.assertEqual(realasdouble(-1), -1.0) - # Function doesn't support classes with __complex__ dunder, see #109598 - #self.assertEqual(realasdouble(Complex()), 4.25) - #self.assertEqual(realasdouble(3.14), 3.14) - #self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) - #self.assertEqual(realasdouble(Float()), 4.25) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(realasdouble(BadComplex2()), 4.25) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(realasdouble(BadFloat2()), 4.25) - self.assertRaises(TypeError, realasdouble, BadComplex()) - self.assertRaises(TypeError, realasdouble, BadFloat()) - self.assertRaises(TypeError, realasdouble, object()) - - # CRASHES realasdouble(NULL) - - def test_imagasdouble(self): - # Test PyComplex_ImagAsDouble() - imagasdouble = _testcapi.complex_imagasdouble - - self.assertEqual(imagasdouble(1+2j), 2.0) - self.assertEqual(imagasdouble(1), 0.0) - self.assertEqual(imagasdouble(-1), 0.0) - # Function doesn't support classes with __complex__ dunder, see #109598 - #self.assertEqual(imagasdouble(Complex()), 0.5) - #self.assertEqual(imagasdouble(3.14), 0.0) - #self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) - #self.assertEqual(imagasdouble(Float()), 0.0) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(imagasdouble(BadComplex2()), 0.5) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(imagasdouble(BadFloat2()), 0.0) - # Function returns 0.0 anyway, see #109598 - #self.assertRaises(TypeError, imagasdouble, BadComplex()) - #self.assertRaises(TypeError, imagasdouble, BadFloat()) - #self.assertRaises(TypeError, imagasdouble, object()) - self.assertEqual(imagasdouble(BadComplex()), 0.0) - self.assertEqual(imagasdouble(BadFloat()), 0.0) - self.assertEqual(imagasdouble(object()), 0.0) - - # CRASHES imagasdouble(NULL) - - def test_asccomplex(self): - # Test PyComplex_AsCComplex() - asccomplex = _testcapi.complex_asccomplex - - self.assertEqual(asccomplex(1+2j), 1.0+2.0j) - self.assertEqual(asccomplex(1), 1.0+0.0j) - self.assertEqual(asccomplex(-1), -1.0+0.0j) - self.assertEqual(asccomplex(Complex()), 4.25+0.5j) - self.assertEqual(asccomplex(3.14), 3.14+0.0j) - self.assertEqual(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) - self.assertEqual(asccomplex(Float()), 4.25+0.0j) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) - self.assertRaises(TypeError, asccomplex, BadComplex()) - self.assertRaises(TypeError, asccomplex, BadFloat()) - self.assertRaises(TypeError, asccomplex, object()) - - # CRASHES asccomplex(NULL) - - if __name__ == "__main__": unittest.main() From 6ead74be9ab94e89adaff85cc571126b5af60d49 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 2 Nov 2023 07:06:30 +0300 Subject: [PATCH 05/15] Add missing coverage tests --- Lib/test/test_capi/test_complex.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 2d913604cc03a4..547f4f750d3f93 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -1,4 +1,5 @@ import unittest +import warnings from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex, FloatSubclass, Float, BadFloat, @@ -102,6 +103,10 @@ def test_asccomplex(self): # Test PyComplex_AsCComplex() asccomplex = _testcapi.complex_asccomplex + class BadComplex3: + def __complex__(self): + raise RuntimeError + self.assertEqual(asccomplex(1+2j), 1.0+2.0j) self.assertEqual(asccomplex(1), 1.0+0.0j) self.assertEqual(asccomplex(-1), -1.0+0.0j) @@ -111,9 +116,13 @@ def test_asccomplex(self): self.assertEqual(asccomplex(Float()), 4.25+0.0j) with self.assertWarns(DeprecationWarning): self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + self.assertRaises(DeprecationWarning, asccomplex, BadComplex2()) with self.assertWarns(DeprecationWarning): self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, BadComplex()) + self.assertRaises(RuntimeError, asccomplex, BadComplex3()) self.assertRaises(TypeError, asccomplex, BadFloat()) self.assertRaises(TypeError, asccomplex, object()) From 67fb2143bf86adcfcdc72aa8a7e4ce16b4331abb Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 2 Nov 2023 19:13:44 +0300 Subject: [PATCH 06/15] Cleanup, add comments --- Lib/test/test_capi/test_complex.py | 79 +++++++++++++++++++----------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 547f4f750d3f93..c7f0655543779f 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -11,6 +11,10 @@ NULL = None +class BadComplex3: + def __complex__(self): + raise RuntimeError + class CAPIComplexTest(unittest.TestCase): def test_check(self): @@ -55,20 +59,29 @@ def test_realasdouble(self): # Test PyComplex_RealAsDouble() realasdouble = _testcapi.complex_realasdouble + # Test subclasses of complex/float self.assertEqual(realasdouble(1+2j), 1.0) - self.assertEqual(realasdouble(1), 1.0) - self.assertEqual(realasdouble(-1), -1.0) + self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) + self.assertEqual(realasdouble(3.14), 3.14) + self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) + + # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 #self.assertEqual(realasdouble(Complex()), 4.25) - #self.assertEqual(realasdouble(3.14), 3.14) - #self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) - #self.assertEqual(realasdouble(Float()), 4.25) + #self.assertRaises(TypeError, realasdouble, BadComplex()) #with self.assertWarns(DeprecationWarning): # self.assertEqual(realasdouble(BadComplex2()), 4.25) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(realasdouble(BadFloat2()), 4.25) - self.assertRaises(TypeError, realasdouble, BadComplex()) + #with warnings.catch_warnings(): + # warnings.simplefilter("error", DeprecationWarning) + # self.assertRaises(DeprecationWarning, realasdouble, BadComplex2()) + #self.assertRaises(RuntimeError, realasdouble, BadComplex3()) + + # Test types with __float__ dunder method + self.assertEqual(realasdouble(Float()), 4.25) self.assertRaises(TypeError, realasdouble, BadFloat()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(realasdouble(BadFloat2()), 4.25) + self.assertRaises(TypeError, realasdouble, object()) # CRASHES realasdouble(NULL) @@ -77,25 +90,32 @@ def test_imagasdouble(self): # Test PyComplex_ImagAsDouble() imagasdouble = _testcapi.complex_imagasdouble + # Test subclasses of complex/float self.assertEqual(imagasdouble(1+2j), 2.0) - self.assertEqual(imagasdouble(1), 0.0) - self.assertEqual(imagasdouble(-1), 0.0) + self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0) + self.assertEqual(imagasdouble(3.14), 0.0) + self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) + + # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 #self.assertEqual(imagasdouble(Complex()), 0.5) - #self.assertEqual(imagasdouble(3.14), 0.0) - #self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) - #self.assertEqual(imagasdouble(Float()), 0.0) + #self.assertRaises(TypeError, imagasdouble, BadComplex()) #with self.assertWarns(DeprecationWarning): # self.assertEqual(imagasdouble(BadComplex2()), 0.5) + #with warnings.catch_warnings(): + # warnings.simplefilter("error", DeprecationWarning) + # self.assertRaises(DeprecationWarning, imagasdouble, BadComplex2()) + #self.assertRaises(RuntimeError, imagasdouble, BadComplex3()) + + # Test types with __float__ dunder method + # Function doesn't support classes with __float__ dunder, see #109598 + #self.assertEqual(imagasdouble(Float()), 0.0) + #self.assertRaises(TypeError, imagasdouble, BadFloat()) #with self.assertWarns(DeprecationWarning): # self.assertEqual(imagasdouble(BadFloat2()), 0.0) + # Function returns 0.0 anyway, see #109598 - #self.assertRaises(TypeError, imagasdouble, BadComplex()) - #self.assertRaises(TypeError, imagasdouble, BadFloat()) #self.assertRaises(TypeError, imagasdouble, object()) - self.assertEqual(imagasdouble(BadComplex()), 0.0) - self.assertEqual(imagasdouble(BadFloat()), 0.0) - self.assertEqual(imagasdouble(object()), 0.0) # CRASHES imagasdouble(NULL) @@ -103,27 +123,28 @@ def test_asccomplex(self): # Test PyComplex_AsCComplex() asccomplex = _testcapi.complex_asccomplex - class BadComplex3: - def __complex__(self): - raise RuntimeError - + # Test subclasses of complex/float self.assertEqual(asccomplex(1+2j), 1.0+2.0j) - self.assertEqual(asccomplex(1), 1.0+0.0j) - self.assertEqual(asccomplex(-1), -1.0+0.0j) - self.assertEqual(asccomplex(Complex()), 4.25+0.5j) + self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j) self.assertEqual(asccomplex(3.14), 3.14+0.0j) self.assertEqual(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) - self.assertEqual(asccomplex(Float()), 4.25+0.0j) + + # Test types with __complex__ dunder method + self.assertEqual(asccomplex(Complex()), 4.25+0.5j) + self.assertRaises(TypeError, asccomplex, BadComplex()) with self.assertWarns(DeprecationWarning): self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) with warnings.catch_warnings(): warnings.simplefilter("error", DeprecationWarning) self.assertRaises(DeprecationWarning, asccomplex, BadComplex2()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) - self.assertRaises(TypeError, asccomplex, BadComplex()) self.assertRaises(RuntimeError, asccomplex, BadComplex3()) + + # Test types with __float__ dunder method + self.assertEqual(asccomplex(Float()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, BadFloat()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) + self.assertRaises(TypeError, asccomplex, object()) # CRASHES asccomplex(NULL) From 7694c60c844b8210416be155c3ab8327b7908bf9 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 14:27:50 +0300 Subject: [PATCH 07/15] Apply suggestions from code review Co-authored-by: Serhiy Storchaka --- Lib/test/test_capi/test_complex.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index c7f0655543779f..384417b4f73f03 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -25,7 +25,7 @@ def test_check(self): self.assertTrue(check(ComplexSubclass(1+2j))) self.assertFalse(check(Complex())) self.assertFalse(check(3)) - self.assertFalse(check([])) + self.assertFalse(check(3.0)) self.assertFalse(check(object())) # CRASHES check(NULL) @@ -38,7 +38,7 @@ def test_checkexact(self): self.assertFalse(checkexact(ComplexSubclass(1+2j))) self.assertFalse(checkexact(Complex())) self.assertFalse(checkexact(3)) - self.assertFalse(checkexact([])) + self.assertFalse(checkexact(3.0)) self.assertFalse(checkexact(object())) # CRASHES checkexact(NULL) @@ -61,12 +61,16 @@ def test_realasdouble(self): # Test subclasses of complex/float self.assertEqual(realasdouble(1+2j), 1.0) + self.assertEqual(realasdouble(-1+0j), -1.0) + self.assertEqual(realasdouble(-1.0), -1.0) + self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) self.assertEqual(realasdouble(3.14), 3.14) self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 + self.assertRaises(TypeError, realsasdouble, Complex()) #self.assertEqual(realasdouble(Complex()), 4.25) #self.assertRaises(TypeError, realasdouble, BadComplex()) #with self.assertWarns(DeprecationWarning): @@ -82,6 +86,7 @@ def test_realasdouble(self): with self.assertWarns(DeprecationWarning): self.assertEqual(realasdouble(BadFloat2()), 4.25) + self.assertRaises(TypeError, realasdouble, 42) self.assertRaises(TypeError, realasdouble, object()) # CRASHES realasdouble(NULL) @@ -92,6 +97,7 @@ def test_imagasdouble(self): # Test subclasses of complex/float self.assertEqual(imagasdouble(1+2j), 2.0) + self.assertEqual(imagasdouble(1-1j), -1.0) self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0) self.assertEqual(imagasdouble(3.14), 0.0) self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) @@ -115,6 +121,7 @@ def test_imagasdouble(self): # self.assertEqual(imagasdouble(BadFloat2()), 0.0) # Function returns 0.0 anyway, see #109598 + self.assertEquals(imagasdouble(object()), 0.0) #self.assertRaises(TypeError, imagasdouble, object()) # CRASHES imagasdouble(NULL) From 70796bd0f95e7c077090aefd642b1eaa034b04fe Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 14:30:02 +0300 Subject: [PATCH 08/15] +1 --- Lib/test/test_capi/test_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 384417b4f73f03..d43ecb15337652 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -63,7 +63,7 @@ def test_realasdouble(self): self.assertEqual(realasdouble(1+2j), 1.0) self.assertEqual(realasdouble(-1+0j), -1.0) self.assertEqual(realasdouble(-1.0), -1.0) - + self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) self.assertEqual(realasdouble(3.14), 3.14) self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) From 5c1cd4f20c04d364250d70f00d73510e1cf362fe Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 15:12:49 +0300 Subject: [PATCH 09/15] Correct typos and wrongly added test --- Lib/test/test_capi/test_complex.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index d43ecb15337652..b037f2d10d6052 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -70,7 +70,7 @@ def test_realasdouble(self): # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 - self.assertRaises(TypeError, realsasdouble, Complex()) + self.assertRaises(TypeError, realasdouble, Complex()) #self.assertEqual(realasdouble(Complex()), 4.25) #self.assertRaises(TypeError, realasdouble, BadComplex()) #with self.assertWarns(DeprecationWarning): @@ -86,7 +86,7 @@ def test_realasdouble(self): with self.assertWarns(DeprecationWarning): self.assertEqual(realasdouble(BadFloat2()), 4.25) - self.assertRaises(TypeError, realasdouble, 42) + self.assertEqual(realasdouble(42), 42.) self.assertRaises(TypeError, realasdouble, object()) # CRASHES realasdouble(NULL) @@ -121,7 +121,7 @@ def test_imagasdouble(self): # self.assertEqual(imagasdouble(BadFloat2()), 0.0) # Function returns 0.0 anyway, see #109598 - self.assertEquals(imagasdouble(object()), 0.0) + self.assertEqual(imagasdouble(object()), 0.0) #self.assertRaises(TypeError, imagasdouble, object()) # CRASHES imagasdouble(NULL) @@ -153,6 +153,7 @@ def test_asccomplex(self): self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, object()) + self.assertEqual(asccomplex(42), 42+0j) # CRASHES asccomplex(NULL) From 20e37e5a96525006a225b414d4ec27a996d1ea69 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 15:14:58 +0300 Subject: [PATCH 10/15] Drop comments --- Lib/test/test_capi/test_complex.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index b037f2d10d6052..e40f7d964e4e95 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -71,14 +71,6 @@ def test_realasdouble(self): # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 self.assertRaises(TypeError, realasdouble, Complex()) - #self.assertEqual(realasdouble(Complex()), 4.25) - #self.assertRaises(TypeError, realasdouble, BadComplex()) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(realasdouble(BadComplex2()), 4.25) - #with warnings.catch_warnings(): - # warnings.simplefilter("error", DeprecationWarning) - # self.assertRaises(DeprecationWarning, realasdouble, BadComplex2()) - #self.assertRaises(RuntimeError, realasdouble, BadComplex3()) # Test types with __float__ dunder method self.assertEqual(realasdouble(Float()), 4.25) @@ -104,25 +96,10 @@ def test_imagasdouble(self): # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 - #self.assertEqual(imagasdouble(Complex()), 0.5) - #self.assertRaises(TypeError, imagasdouble, BadComplex()) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(imagasdouble(BadComplex2()), 0.5) - #with warnings.catch_warnings(): - # warnings.simplefilter("error", DeprecationWarning) - # self.assertRaises(DeprecationWarning, imagasdouble, BadComplex2()) - #self.assertRaises(RuntimeError, imagasdouble, BadComplex3()) - - # Test types with __float__ dunder method - # Function doesn't support classes with __float__ dunder, see #109598 - #self.assertEqual(imagasdouble(Float()), 0.0) - #self.assertRaises(TypeError, imagasdouble, BadFloat()) - #with self.assertWarns(DeprecationWarning): - # self.assertEqual(imagasdouble(BadFloat2()), 0.0) + self.assertEqual(imagasdouble(Complex()), 0.0) # Function returns 0.0 anyway, see #109598 self.assertEqual(imagasdouble(object()), 0.0) - #self.assertRaises(TypeError, imagasdouble, object()) # CRASHES imagasdouble(NULL) From b743f4b4f83a3202edcb7f16e3fa2f00ef64903b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 15:26:26 +0300 Subject: [PATCH 11/15] Use PyComplex_AsCComplex() in complex_fromccomplex() --- Modules/_testcapi/complex.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index e4e76bbd5ec8d7..beadf892349f63 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -19,7 +19,15 @@ complex_checkexact(PyObject *Py_UNUSED(module), PyObject *obj) static PyObject * complex_fromccomplex(PyObject *Py_UNUSED(module), PyObject *obj) { - Py_complex complex = ((PyComplexObject*)obj)->cval; + Py_complex complex; + + + NULLABLE(obj); + complex = PyComplex_AsCComplex(obj); + + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } return PyComplex_FromCComplex(complex); } From 34347fad715295b4023b703d707f52ed7cd7a737 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 4 Nov 2023 18:28:05 +0300 Subject: [PATCH 12/15] Use PyArg_Parse('D') instead --- Modules/_testcapi/complex.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index beadf892349f63..400f4054c613ee 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -21,11 +21,7 @@ complex_fromccomplex(PyObject *Py_UNUSED(module), PyObject *obj) { Py_complex complex; - - NULLABLE(obj); - complex = PyComplex_AsCComplex(obj); - - if (complex.real == -1. && PyErr_Occurred()) { + if (!PyArg_Parse(obj, "D", &complex)) { return NULL; } From 09f52463b8b7e35c220ca7515a3be4e8ee660d2d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 5 Nov 2023 04:43:01 +0300 Subject: [PATCH 13/15] s/3.14/4.25/g --- Lib/test/test_capi/test_complex.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index e40f7d964e4e95..2e0b4619d99184 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -65,8 +65,8 @@ def test_realasdouble(self): self.assertEqual(realasdouble(-1.0), -1.0) self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) - self.assertEqual(realasdouble(3.14), 3.14) - self.assertEqual(realasdouble(FloatSubclass(3.14)), 3.14) + self.assertEqual(realasdouble(4.25), 4.25) + self.assertEqual(realasdouble(FloatSubclass(4.25)), 4.25) # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 @@ -91,8 +91,8 @@ def test_imagasdouble(self): self.assertEqual(imagasdouble(1+2j), 2.0) self.assertEqual(imagasdouble(1-1j), -1.0) self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0) - self.assertEqual(imagasdouble(3.14), 0.0) - self.assertEqual(imagasdouble(FloatSubclass(3.14)), 0.0) + self.assertEqual(imagasdouble(4.25), 0.0) + self.assertEqual(imagasdouble(FloatSubclass(4.25)), 0.0) # Test types with __complex__ dunder method # Function doesn't support classes with __complex__ dunder, see #109598 @@ -110,8 +110,8 @@ def test_asccomplex(self): # Test subclasses of complex/float self.assertEqual(asccomplex(1+2j), 1.0+2.0j) self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j) - self.assertEqual(asccomplex(3.14), 3.14+0.0j) - self.assertEqual(asccomplex(FloatSubclass(3.14)), 3.14+0.0j) + self.assertEqual(asccomplex(4.25), 4.25+0.0j) + self.assertEqual(asccomplex(FloatSubclass(4.25)), 4.25+0.0j) # Test types with __complex__ dunder method self.assertEqual(asccomplex(Complex()), 4.25+0.5j) From 3682e5135c2860a97972d9bdcb2753f05b84df30 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 5 Nov 2023 04:49:35 +0300 Subject: [PATCH 14/15] Reorder statements in test_as* and add an integer test for imagasdouble() --- Lib/test/test_capi/test_complex.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 2e0b4619d99184..96d79cd7f69e13 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -59,13 +59,14 @@ def test_realasdouble(self): # Test PyComplex_RealAsDouble() realasdouble = _testcapi.complex_realasdouble - # Test subclasses of complex/float self.assertEqual(realasdouble(1+2j), 1.0) self.assertEqual(realasdouble(-1+0j), -1.0) self.assertEqual(realasdouble(-1.0), -1.0) + self.assertEqual(realasdouble(4.25), 4.25) + self.assertEqual(realasdouble(42), 42.) + # Test subclasses of complex/float self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) - self.assertEqual(realasdouble(4.25), 4.25) self.assertEqual(realasdouble(FloatSubclass(4.25)), 4.25) # Test types with __complex__ dunder method @@ -78,7 +79,6 @@ def test_realasdouble(self): with self.assertWarns(DeprecationWarning): self.assertEqual(realasdouble(BadFloat2()), 4.25) - self.assertEqual(realasdouble(42), 42.) self.assertRaises(TypeError, realasdouble, object()) # CRASHES realasdouble(NULL) @@ -87,11 +87,13 @@ def test_imagasdouble(self): # Test PyComplex_ImagAsDouble() imagasdouble = _testcapi.complex_imagasdouble - # Test subclasses of complex/float self.assertEqual(imagasdouble(1+2j), 2.0) self.assertEqual(imagasdouble(1-1j), -1.0) - self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0) self.assertEqual(imagasdouble(4.25), 0.0) + self.assertEqual(imagasdouble(42), 0.0) + + # Test subclasses of complex/float + self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0) self.assertEqual(imagasdouble(FloatSubclass(4.25)), 0.0) # Test types with __complex__ dunder method @@ -107,10 +109,12 @@ def test_asccomplex(self): # Test PyComplex_AsCComplex() asccomplex = _testcapi.complex_asccomplex - # Test subclasses of complex/float self.assertEqual(asccomplex(1+2j), 1.0+2.0j) - self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j) self.assertEqual(asccomplex(4.25), 4.25+0.0j) + self.assertEqual(asccomplex(42), 42+0j) + + # Test subclasses of complex/float + self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j) self.assertEqual(asccomplex(FloatSubclass(4.25)), 4.25+0.0j) # Test types with __complex__ dunder method @@ -130,7 +134,6 @@ def test_asccomplex(self): self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, object()) - self.assertEqual(asccomplex(42), 42+0j) # CRASHES asccomplex(NULL) From a4981d6534635d14d40f8db246710287848b4ce7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 5 Nov 2023 09:17:41 +0200 Subject: [PATCH 15/15] Apply suggestions from code review --- Lib/test/test_capi/test_complex.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 96d79cd7f69e13..9f51efb091dea5 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -61,9 +61,10 @@ def test_realasdouble(self): self.assertEqual(realasdouble(1+2j), 1.0) self.assertEqual(realasdouble(-1+0j), -1.0) - self.assertEqual(realasdouble(-1.0), -1.0) self.assertEqual(realasdouble(4.25), 4.25) + self.assertEqual(realasdouble(-1.0), -1.0) self.assertEqual(realasdouble(42), 42.) + self.assertEqual(realasdouble(-1), -1.0) # Test subclasses of complex/float self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0) @@ -110,8 +111,11 @@ def test_asccomplex(self): asccomplex = _testcapi.complex_asccomplex self.assertEqual(asccomplex(1+2j), 1.0+2.0j) + self.assertEqual(asccomplex(-1+2j), -1.0+2.0j) self.assertEqual(asccomplex(4.25), 4.25+0.0j) + self.assertEqual(asccomplex(-1.0), -1.0+0.0j) self.assertEqual(asccomplex(42), 42+0j) + self.assertEqual(asccomplex(-1), -1.0+0.0j) # Test subclasses of complex/float self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j)