From 5c606eef30da7bac0b9dc91430447efae34abe51 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 17 Apr 2026 14:28:03 +0300 Subject: [PATCH] Revert "gh-146151: Add support for complex arrays in the array module (#146237)" This reverts commit 0e3b3b895c82b7e17ddca98bceaeb6be7f34a5ac. --- Doc/library/array.rst | 21 +--- Doc/whatsnew/3.15.rst | 4 - Lib/test/test_array.py | 83 ++----------- Modules/arraymodule.c | 206 +++------------------------------ Modules/clinic/arraymodule.c.h | 7 +- 5 files changed, 30 insertions(+), 291 deletions(-) diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 4468edb6efa..251b7d114da 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -9,7 +9,7 @@ -------------- This module defines an object type which can compactly represent an array of -basic values: characters, integers, floating-point numbers, complex numbers. Arrays are mutable :term:`sequence` +basic values: characters, integers, floating-point numbers. Arrays are mutable :term:`sequence` types and behave very much like lists, except that the type of objects stored in them is constrained. The type is specified at object creation time by using a :dfn:`type code`, which is a single character. The following type codes are @@ -48,11 +48,6 @@ defined: +-----------+--------------------+-------------------+-----------------------+-------+ | ``'d'`` | double | float | 8 | | +-----------+--------------------+-------------------+-----------------------+-------+ -| ``'F'`` | float complex | complex | 8 | \(4) | -+-----------+--------------------+-------------------+-----------------------+-------+ -| ``'D'`` | double complex | complex | 16 | \(4) | -+-----------+--------------------+-------------------+-----------------------+-------+ - Notes: @@ -79,15 +74,6 @@ Notes: .. versionadded:: 3.15 -(4) - Complex types (``F`` and ``D``) are available unconditionally, - regardless on support for complex types (the Annex G of the C11 standard) - by the C compiler. - As specified in the C11 standard, each complex type is represented by a - two-element C array containing, respectively, the real and imaginary parts. - - .. versionadded:: 3.15 - .. seealso:: The :ref:`ctypes ` and @@ -171,10 +157,9 @@ The module defines the following type: .. method:: byteswap() "Byteswap" all items of the array. This is only supported for values which are - 1, 2, 4, 8 or 16 bytes in size; for other types of values, :exc:`RuntimeError` is + 1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is raised. It is useful when reading data from a file written on a machine with a - different byte order. Note, that for complex types the order of - components (the real part, followed by imaginary part) is preserved. + different byte order. .. method:: count(value, /) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 7ea7c901ece..adadb910b29 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -678,10 +678,6 @@ argparse array ----- -* Support the :c:expr:`float complex` and :c:expr:`double complex` C types: - formatting characters ``'F'`` and ``'D'`` respectively. - (Contributed by Sergey B Kirpichev in :gh:`146151`.) - * Support half-floats (16-bit IEEE 754 binary interchange format): formatting character ``'e'``. (Contributed by Sergey B Kirpichev in :gh:`146238`.) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 4493349798d..012078044bd 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -31,7 +31,7 @@ class ArraySubclassWithKwargs(array.array): def __init__(self, typecode, newarg=None): array.array.__init__(self) -typecodes = 'uwbBhHiIlLfdqQFDe' +typecodes = 'uwbBhHiIlLfdqQe' class MiscTest(unittest.TestCase): @@ -113,14 +113,10 @@ def __index__(self): UTF16_BE = 19 UTF32_LE = 20 UTF32_BE = 21 -IEEE_754_FLOAT_COMPLEX_LE = 22 -IEEE_754_FLOAT_COMPLEX_BE = 23 -IEEE_754_DOUBLE_COMPLEX_LE = 24 -IEEE_754_DOUBLE_COMPLEX_BE = 25 -IEEE_754_FLOAT16_LE = 26 -IEEE_754_FLOAT16_BE = 27 +IEEE_754_FLOAT16_LE = 22 +IEEE_754_FLOAT16_BE = 23 -MACHINE_FORMAT_CODE_MAX = 27 +MACHINE_FORMAT_CODE_MAX = 23 class ArrayReconstructorTest(unittest.TestCase): @@ -147,7 +143,7 @@ def test_error(self): self.assertRaises(ValueError, array_reconstructor, array.array, "b", UNKNOWN_FORMAT, b"") self.assertRaises(ValueError, array_reconstructor, - array.array, "b", MACHINE_FORMAT_CODE_MAX + 1, b"") + array.array, "b", 22, b"") self.assertRaises(ValueError, array_reconstructor, array.array, "d", 16, b"a") @@ -199,15 +195,7 @@ def test_numbers(self): (['d'], IEEE_754_DOUBLE_LE, 'dddd', - [9006104071832581.0, float('inf'), float('-inf'), -0.0]), - (['F'], IEEE_754_FLOAT_COMPLEX_LE, 'FFFF', - [16711938.0j, float('inf'), complex('1-infj'), -0.0]), - (['D'], IEEE_754_DOUBLE_COMPLEX_LE, 'DDDD', - [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]), + [9006104071832581.0, float('inf'), float('-inf'), -0.0]) ) for testcase in testcases: valid_typecodes, mformat_code, struct_fmt, values = testcase @@ -295,7 +283,7 @@ def test_byteswap(self): example = self.example a = array.array(self.typecode, example) self.assertRaises(TypeError, a.byteswap, 42) - if a.itemsize in (1, 2, 4, 8, 16): + if a.itemsize in (1, 2, 4, 8): b = array.array(self.typecode, example) b.byteswap() if a.itemsize==1: @@ -1541,54 +1529,6 @@ def test_byteswap(self): b.byteswap() self.assertEqual(a, b) -class CFPTest(NumberTest): - example = [-42j, 0, 42+1j, 1e5j, -1e10] - outside = 23 - - def assertEntryEqual(self, entry1, entry2): - self.assertAlmostEqual(entry1, entry2) - - def test_cmp(self): - a = array.array(self.typecode, self.example) - self.assertIs(a == 42, False) - self.assertIs(a != 42, True) - - self.assertIs(a == a, True) - self.assertIs(a != a, False) - self.assertIs(a < a, False) - self.assertIs(a <= a, True) - self.assertIs(a > a, False) - self.assertIs(a >= a, True) - - self.assertIs(a == 2*a, False) - self.assertIs(a != 2*a, True) - self.assertIs(a < 2*a, True) - self.assertIs(a <= 2*a, True) - self.assertIs(a > 2*a, False) - self.assertIs(a >= 2*a, False) - - def test_nan(self): - a = array.array(self.typecode, [float('nan')]) - b = array.array(self.typecode, [float('nan')]) - self.assertIs(a != b, True) - self.assertIs(a == b, False) - - def test_byteswap(self): - a = array.array(self.typecode, self.example) - self.assertRaises(TypeError, a.byteswap, 42) - if a.itemsize in (1, 2, 4, 8): - b = array.array(self.typecode, self.example) - b.byteswap() - if a.itemsize == 1: - self.assertEqual(a, b) - else: - # On alphas treating the byte swapped bit patterns as - # floats/doubles results in floating-point exceptions - # => compare the 8bit string values instead - self.assertNotEqual(a.tobytes(), b.tobytes()) - b.byteswap() - self.assertEqual(a, b) - class HalfFloatTest(FPTest, unittest.TestCase): example = [-42.0, 0, 42, 1e2, -1e4] @@ -1623,15 +1563,6 @@ def test_alloc_overflow(self): self.fail("Array of size > maxsize created - MemoryError expected") -class ComplexFloatTest(CFPTest, unittest.TestCase): - typecode = 'F' - minitemsize = 8 - -class ComplexDoubleTest(CFPTest, unittest.TestCase): - typecode = 'D' - minitemsize = 16 - - class LargeArrayTest(unittest.TestCase): typecode = 'b' diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index a86a7561271..341f238374b 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -117,15 +117,11 @@ enum machine_format_code { UTF16_BE = 19, UTF32_LE = 20, UTF32_BE = 21, - IEEE_754_FLOAT_COMPLEX_LE = 22, - IEEE_754_FLOAT_COMPLEX_BE = 23, - IEEE_754_DOUBLE_COMPLEX_LE = 24, - IEEE_754_DOUBLE_COMPLEX_BE = 25, - IEEE_754_FLOAT16_LE = 26, - IEEE_754_FLOAT16_BE = 27 + IEEE_754_FLOAT16_LE = 22, + IEEE_754_FLOAT16_BE = 23 }; #define MACHINE_FORMAT_CODE_MIN 0 -#define MACHINE_FORMAT_CODE_MAX 27 +#define MACHINE_FORMAT_CODE_MAX 23 /* @@ -680,64 +676,6 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) return 0; } -static PyObject * -cf_getitem(arrayobject *ap, Py_ssize_t i) -{ - float f[2]; - - memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f)); - return PyComplex_FromDoubles(f[0], f[1]); -} - -static int -cf_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) -{ - Py_complex x; - float f[2]; - - if (!PyArg_Parse(v, "D;array item must be complex", &x)) { - return -1; - } - - CHECK_ARRAY_BOUNDS(ap, i); - - f[0] = (float)x.real; - f[1] = (float)x.imag; - if (i >= 0) { - memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); - } - return 0; -} - -static PyObject * -cd_getitem(arrayobject *ap, Py_ssize_t i) -{ - double f[2]; - - memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f)); - return PyComplex_FromDoubles(f[0], f[1]); -} - -static int -cd_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) -{ - Py_complex x; - double f[2]; - - if (!PyArg_Parse(v, "D;array item must be complex", &x)) { - return -1; - } - - CHECK_ARRAY_BOUNDS(ap, i); - - f[0] = x.real; - f[1] = x.imag; - if (i >= 0) { - memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); - } - return 0; -} - #define DEFINE_COMPAREITEMS(code, type) \ static int \ code##_compareitems(const void *lhs, const void *rhs, Py_ssize_t length) \ @@ -783,8 +721,6 @@ static const struct arraydescr descriptors[] = { {'e', sizeof(short), e_getitem, e_setitem, NULL, "e", 0, 0}, {'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0}, {'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 0, 0}, - {'F', 2*sizeof(float), cf_getitem, cf_setitem, NULL, "F", 0, 0}, - {'D', 2*sizeof(double), cd_getitem, cd_setitem, NULL, "D", 0, 0}, {'\0', 0, 0, 0, 0, 0, 0} /* Sentinel */ }; @@ -1578,14 +1514,13 @@ array.array.byteswap Byteswap all items of the array. -If the items in the array are not 1, 2, 4, 8 or 16 bytes in size, RuntimeError -is raised. Note, that for complex types the order of -components (the real part, followed by imaginary part) is preserved. +If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is +raised. [clinic start generated code]*/ static PyObject * array_array_byteswap_impl(arrayobject *self) -/*[clinic end generated code: output=5f8236cbdf0d90b5 input=aafda275f48191d0]*/ +/*[clinic end generated code: output=5f8236cbdf0d90b5 input=9af1d1749000b14f]*/ { char *p; Py_ssize_t i; @@ -1611,66 +1546,19 @@ array_array_byteswap_impl(arrayobject *self) } break; case 8: - if (self->ob_descr->typecode != 'F') { - for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { - char p0 = p[0]; - char p1 = p[1]; - char p2 = p[2]; - char p3 = p[3]; - p[0] = p[7]; - p[1] = p[6]; - p[2] = p[5]; - p[3] = p[4]; - p[4] = p3; - p[5] = p2; - p[6] = p1; - p[7] = p0; - } - } - else { - for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { - char t0 = p[0]; - char t1 = p[1]; - p[0] = p[3]; - p[1] = p[2]; - p[2] = t1; - p[3] = t0; - t0 = p[4]; - t1 = p[5]; - p[4] = p[7]; - p[5] = p[6]; - p[6] = t1; - p[7] = t0; - } - } - break; - case 16: - assert(self->ob_descr->typecode == 'D'); for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { - char t0 = p[0]; - char t1 = p[1]; - char t2 = p[2]; - char t3 = p[3]; + char p0 = p[0]; + char p1 = p[1]; + char p2 = p[2]; + char p3 = p[3]; p[0] = p[7]; p[1] = p[6]; p[2] = p[5]; p[3] = p[4]; - p[4] = t3; - p[5] = t2; - p[6] = t1; - p[7] = t0; - t0 = p[8]; - t1 = p[9]; - t2 = p[10]; - t3 = p[11]; - p[8] = p[15]; - p[9] = p[14]; - p[10] = p[13]; - p[11] = p[12]; - p[12] = t3; - p[13] = t2; - p[14] = t1; - p[15] = t0; + p[4] = p3; + p[5] = p2; + p[6] = p1; + p[7] = p0; } break; default: @@ -2106,12 +1994,8 @@ static const struct mformatdescr { {4, 0, 1}, /* 19: UTF16_BE */ {8, 0, 0}, /* 20: UTF32_LE */ {8, 0, 1}, /* 21: UTF32_BE */ - {8, 0, 0}, /* 22: IEEE_754_FLOAT_COMPLEX_LE */ - {8, 0, 1}, /* 23: IEEE_754_FLOAT_COMPLEX_BE */ - {16, 0, 0}, /* 24: IEEE_754_DOUBLE_COMPLEX_LE */ - {16, 0, 1}, /* 25: IEEE_754_DOUBLE_COMPLEX_BE */ - {2, 0, 0}, /* 26: IEEE_754_FLOAT16_LE */ - {2, 0, 1} /* 27: IEEE_754_FLOAT16_BE */ + {2, 0, 0}, /* 22: IEEE_754_FLOAT16_LE */ + {2, 0, 1} /* 23: IEEE_754_FLOAT16_BE */ }; @@ -2155,14 +2039,6 @@ typecode_to_mformat_code(char typecode) case 'd': return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_DOUBLE_BE : IEEE_754_DOUBLE_LE; - case 'F': - return _PY_FLOAT_BIG_ENDIAN ? \ - IEEE_754_FLOAT_COMPLEX_BE : IEEE_754_FLOAT_COMPLEX_LE; - - case 'D': - return _PY_FLOAT_BIG_ENDIAN ? \ - IEEE_754_DOUBLE_COMPLEX_BE : IEEE_754_DOUBLE_COMPLEX_LE; - /* Integers */ case 'h': intsize = sizeof(short); @@ -2394,52 +2270,6 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, } break; } - case IEEE_754_FLOAT_COMPLEX_LE: - case IEEE_754_FLOAT_COMPLEX_BE: { - Py_ssize_t i; - int le = (mformat_code == IEEE_754_FLOAT_COMPLEX_LE) ? 1 : 0; - Py_ssize_t itemcount = Py_SIZE(items) / 8; - const char *memstr = PyBytes_AS_STRING(items); - - converted_items = PyList_New(itemcount); - if (converted_items == NULL) { - return NULL; - } - for (i = 0; i < itemcount; i++) { - PyObject *pycomplex = PyComplex_FromDoubles( - PyFloat_Unpack4(&memstr[i * 8], le), - PyFloat_Unpack4(&memstr[i * 8] + 4, le)); - if (pycomplex == NULL) { - Py_DECREF(converted_items); - return NULL; - } - PyList_SET_ITEM(converted_items, i, pycomplex); - } - break; - } - case IEEE_754_DOUBLE_COMPLEX_LE: - case IEEE_754_DOUBLE_COMPLEX_BE: { - Py_ssize_t i; - int le = (mformat_code == IEEE_754_DOUBLE_COMPLEX_LE) ? 1 : 0; - Py_ssize_t itemcount = Py_SIZE(items) / 16; - const char *memstr = PyBytes_AS_STRING(items); - - converted_items = PyList_New(itemcount); - if (converted_items == NULL) { - return NULL; - } - for (i = 0; i < itemcount; i++) { - PyObject *pycomplex = PyComplex_FromDoubles( - PyFloat_Unpack8(&memstr[i * 16], le), - PyFloat_Unpack8(&memstr[i * 16] + 8, le)); - if (pycomplex == NULL) { - Py_DECREF(converted_items); - return NULL; - } - PyList_SET_ITEM(converted_items, i, pycomplex); - } - break; - } case UTF16_LE: case UTF16_BE: { int byteorder = (mformat_code == UTF16_LE) ? -1 : 1; @@ -3145,7 +2975,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyDoc_STRVAR(module_doc, "This module defines an object type which can efficiently represent\n\ an array of basic values: characters, integers, floating-point\n\ -numbers, complex numbers. Arrays are sequence types and behave very much like lists,\n\ +numbers. Arrays are sequence types and behave very much like lists,\n\ except that the type of objects stored in them is constrained.\n"); PyDoc_STRVAR(arraytype_doc, @@ -3175,8 +3005,6 @@ The following type codes are defined:\n\ 'e' 16-bit IEEE floats 2\n\ 'f' floating-point 4\n\ 'd' floating-point 8\n\ - 'F' float complex 8\n\ - 'D' double complex 16\n\ \n\ NOTE: The 'u' typecode corresponds to Python's unicode character. On\n\ narrow builds this is 2-bytes on wide builds this is 4-bytes.\n\ diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index 8a3fb4b515e..2648583c654 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -335,9 +335,8 @@ PyDoc_STRVAR(array_array_byteswap__doc__, "\n" "Byteswap all items of the array.\n" "\n" -"If the items in the array are not 1, 2, 4, 8 or 16 bytes in size, RuntimeError\n" -"is raised. Note, that for complex types the order of\n" -"components (the real part, followed by imaginary part) is preserved."); +"If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is\n" +"raised."); #define ARRAY_ARRAY_BYTESWAP_METHODDEF \ {"byteswap", (PyCFunction)array_array_byteswap, METH_NOARGS, array_array_byteswap__doc__}, @@ -779,4 +778,4 @@ array_arrayiterator___setstate__(PyObject *self, PyObject *state) return return_value; } -/*[clinic end generated code: output=9dcb2fc40710f83d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c993c3598085840e input=a9049054013a1b77]*/