Revert "gh-146151: Add support for complex arrays in the array module (#146237)"

This reverts commit 0e3b3b895c.
This commit is contained in:
Sergey B Kirpichev 2026-04-17 14:28:03 +03:00
parent 2a07ff980b
commit 5c606eef30
5 changed files with 30 additions and 291 deletions

View file

@ -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 <ctypes-fundamental-data-types>` 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, /)

View file

@ -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`.)

View file

@ -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]),
(['d'], IEEE_754_DOUBLE_BE, '>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]),
(['F'], IEEE_754_FLOAT_COMPLEX_BE, '>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]),
(['D'], IEEE_754_DOUBLE_COMPLEX_BE, '>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'

View file

@ -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\

View file

@ -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]*/