https://github.com/python/cpython/commit/f425509349fecd3a8177502a2d4aafca8eef040a
commit: f425509349fecd3a8177502a2d4aafca8eef040a
branch: main
author: Sergey B Kirpichev <skirpic...@gmail.com>
committer: encukou <encu...@gmail.com>
date: 2025-05-02T18:24:52+02:00
summary:

gh-121249: unconditionally support `complex` types in `struct` (GH-132864)


Co-authored-by: Lisandro Dalcin <dalc...@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picn...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst
M Doc/library/struct.rst
M Doc/whatsnew/3.14.rst
M Lib/test/test_struct.py
M Modules/_struct.c

diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst
index 071b90b9359fa5..66a1fdea54d857 100644
--- a/Doc/library/struct.rst
+++ b/Doc/library/struct.rst
@@ -260,6 +260,10 @@ platform-dependent.
 
+--------+--------------------------+--------------------+----------------+------------+
 | ``d``  | :c:expr:`double`         | float              | 8              | 
\(4)       |
 
+--------+--------------------------+--------------------+----------------+------------+
+| ``F``  | :c:expr:`float complex`  | complex            | 8              | 
\(10)      |
++--------+--------------------------+--------------------+----------------+------------+
+| ``D``  | :c:expr:`double complex` | complex            | 16             | 
\(10)      |
++--------+--------------------------+--------------------+----------------+------------+
 | ``s``  | :c:expr:`char[]`         | bytes              |                | 
\(9)       |
 
+--------+--------------------------+--------------------+----------------+------------+
 | ``p``  | :c:expr:`char[]`         | bytes              |                | 
\(8)       |
@@ -267,17 +271,6 @@ platform-dependent.
 | ``P``  | :c:expr:`void \*`        | integer            |                | 
\(5)       |
 
+--------+--------------------------+--------------------+----------------+------------+
 
-Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
-C11 standard) is supported, the following format characters are available:
-
-+--------+--------------------------+--------------------+----------------+------------+
-| Format | C Type                   | Python type        | Standard size  | 
Notes      |
-+========+==========================+====================+================+============+
-| ``F``  | :c:expr:`float complex`  | complex            | 8              | 
\(10)      |
-+--------+--------------------------+--------------------+----------------+------------+
-| ``D``  | :c:expr:`double complex` | complex            | 16             | 
\(10)      |
-+--------+--------------------------+--------------------+----------------+------------+
-
 .. versionchanged:: 3.3
    Added support for the ``'n'`` and ``'N'`` formats.
 
@@ -367,6 +360,11 @@ Notes:
    For the ``'E'`` and ``'C'`` format characters, the packed representation 
uses
    the IEEE 754 binary32 and binary64 format for components of the complex
    number, regardless of the floating-point format used by the platform.
+   Note that complex types (``F`` and ``D``) are available unconditionally,
+   despite complex types being an optional feature in C.
+   As specified in the C11 standard, each complex type is represented by a
+   two-element C array containing, respectively, the real and imaginary parts.
+
 
 A format character may be preceded by an integral repeat count.  For example,
 the format string ``'4h'`` means exactly the same as ``'hhhh'``.
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 460b77a2385911..62dd0551483e97 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1313,8 +1313,8 @@ struct
 ------
 
 * Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
-  the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``,
-  respectively) if the compiler has C11 complex arithmetic.
+  the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``
+  respectively).
   (Contributed by Sergey B Kirpichev in :gh:`121249`.)
 
 
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index a410fd5a1940d1..7df01f28f09118 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -22,12 +22,6 @@
 INF = float('inf')
 NAN = float('nan')
 
-try:
-    struct.pack('D', 1j)
-    have_c_complex = True
-except struct.error:
-    have_c_complex = False
-
 def iter_integer_formats(byteorders=byteorders):
     for code in integer_codes:
         for byteorder in byteorders:
@@ -796,7 +790,6 @@ def test_repr(self):
         s = struct.Struct('=i2H')
         self.assertEqual(repr(s), f'Struct({s.format!r})')
 
-    @unittest.skipUnless(have_c_complex, "requires C11 complex type support")
     def test_c_complex_round_trip(self):
         values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
                                                      -3, INF, -INF, NAN], 2)]
@@ -806,19 +799,6 @@ def test_c_complex_round_trip(self):
                     round_trip = struct.unpack(f, struct.pack(f, z))[0]
                     self.assertComplexesAreIdentical(z, round_trip)
 
-    @unittest.skipIf(have_c_complex, "requires no C11 complex type support")
-    def test_c_complex_error(self):
-        msg1 = "'F' format not supported on this system"
-        msg2 = "'D' format not supported on this system"
-        with self.assertRaisesRegex(struct.error, msg1):
-            struct.pack('F', 1j)
-        with self.assertRaisesRegex(struct.error, msg1):
-            struct.unpack('F', b'1')
-        with self.assertRaisesRegex(struct.error, msg2):
-            struct.pack('D', 1j)
-        with self.assertRaisesRegex(struct.error, msg2):
-            struct.unpack('D', b'1')
-
 
 class UnpackIteratorTest(unittest.TestCase):
     """
diff --git 
a/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst 
b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst
new file mode 100644
index 00000000000000..28140ab30544b2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst
@@ -0,0 +1,2 @@
+Always support the :c:expr:`float complex` and :c:expr:`double complex` C 
types in
+the :mod:`struct` module.  Patch by Sergey B Kirpichev.
diff --git a/Modules/_struct.c b/Modules/_struct.c
index ee757ed8a9d29d..e400f607b558dc 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -12,9 +12,6 @@
 #include "pycore_long.h"          // _PyLong_AsByteArray()
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 
-#ifdef Py_HAVE_C_COMPLEX
-#  include "_complex.h"           // complex
-#endif
 #include <stddef.h>               // offsetof()
 
 /*[clinic input]
@@ -495,25 +492,23 @@ nu_double(_structmodulestate *state, const char *p, const 
formatdef *f)
     return PyFloat_FromDouble(x);
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static PyObject *
 nu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
 {
-    float complex x;
+    float x[2];
 
     memcpy(&x, p, sizeof(x));
-    return PyComplex_FromDoubles(creal(x), cimag(x));
+    return PyComplex_FromDoubles(x[0], x[1]);
 }
 
 static PyObject *
 nu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
 {
-    double complex x;
+    double x[2];
 
     memcpy(&x, p, sizeof(x));
-    return PyComplex_FromDoubles(creal(x), cimag(x));
+    return PyComplex_FromDoubles(x[0], x[1]);
 }
-#endif
 
 static PyObject *
 nu_void_p(_structmodulestate *state, const char *p, const formatdef *f)
@@ -788,13 +783,12 @@ np_double(_structmodulestate *state, char *p, PyObject 
*v, const formatdef *f)
     return 0;
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static int
 np_float_complex(_structmodulestate *state, char *p, PyObject *v,
                  const formatdef *f)
 {
     Py_complex c = PyComplex_AsCComplex(v);
-    float complex x = CMPLXF((float)c.real, (float)c.imag);
+    float x[2] = {(float)c.real, (float)c.imag};
 
     if (c.real == -1 && PyErr_Occurred()) {
         PyErr_SetString(state->StructError,
@@ -810,7 +804,7 @@ np_double_complex(_structmodulestate *state, char *p, 
PyObject *v,
                   const formatdef *f)
 {
     Py_complex c = PyComplex_AsCComplex(v);
-    double complex x = CMPLX(c.real, c.imag);
+    double x[2] = {c.real, c.imag};
 
     if (c.real == -1 && PyErr_Occurred()) {
         PyErr_SetString(state->StructError,
@@ -820,25 +814,6 @@ np_double_complex(_structmodulestate *state, char *p, 
PyObject *v,
     memcpy(p, &x, sizeof(x));
     return 0;
 }
-#else
-static int
-np_complex_stub(_structmodulestate *state, char *p, PyObject *v,
-                const formatdef *f)
-{
-    PyErr_Format(state->StructError,
-                 "'%c' format not supported on this system",
-                 f->format);
-    return -1;
-}
-static PyObject *
-nu_complex_stub(_structmodulestate *state, const char *p, const formatdef *f)
-{
-    PyErr_Format(state->StructError,
-                 "'%c' format not supported on this system",
-                 f->format);
-    return NULL;
-}
-#endif
 
 static int
 np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -878,13 +853,8 @@ static const formatdef native_table[] = {
     {'e',       sizeof(short),  _Alignof(short),    nu_halffloat,   
np_halffloat},
     {'f',       sizeof(float),  _Alignof(float),    nu_float,       np_float},
     {'d',       sizeof(double), _Alignof(double),   nu_double,      np_double},
-#ifdef Py_HAVE_C_COMPLEX
-    {'F',       sizeof(float complex), _Alignof(float complex), 
nu_float_complex, np_float_complex},
-    {'D',       sizeof(double complex), _Alignof(double complex), 
nu_double_complex, np_double_complex},
-#else
-    {'F',       1, 0, nu_complex_stub, np_complex_stub},
-    {'D',       1, 0, nu_complex_stub, np_complex_stub},
-#endif
+    {'F',       2*sizeof(float), _Alignof(float[2]), nu_float_complex, 
np_float_complex},
+    {'D',       2*sizeof(double), _Alignof(double[2]), nu_double_complex, 
np_double_complex},
     {'P',       sizeof(void *), _Alignof(void *),   nu_void_p,      np_void_p},
     {0}
 };
@@ -985,7 +955,6 @@ bu_double(_structmodulestate *state, const char *p, const 
formatdef *f)
     return unpack_double(p, 0);
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static PyObject *
 bu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
 {
@@ -1015,7 +984,6 @@ bu_double_complex(_structmodulestate *state, const char 
*p, const formatdef *f)
     }
     return PyComplex_FromDoubles(x, y);
 }
-#endif
 
 static PyObject *
 bu_bool(_structmodulestate *state, const char *p, const formatdef *f)
@@ -1156,7 +1124,6 @@ bp_double(_structmodulestate *state, char *p, PyObject 
*v, const formatdef *f)
     return PyFloat_Pack8(x, p, 0);
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static int
 bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const 
formatdef *f)
 {
@@ -1186,7 +1153,6 @@ bp_double_complex(_structmodulestate *state, char *p, 
PyObject *v, const formatd
     }
     return PyFloat_Pack8(x.imag, p + 8, 0);
 }
-#endif
 
 static int
 bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1218,13 +1184,8 @@ static formatdef bigendian_table[] = {
     {'e',       2,              0,              bu_halffloat,   bp_halffloat},
     {'f',       4,              0,              bu_float,       bp_float},
     {'d',       8,              0,              bu_double,      bp_double},
-#ifdef Py_HAVE_C_COMPLEX
     {'F',       8,              0,              bu_float_complex, 
bp_float_complex},
     {'D',       16,             0,              bu_double_complex, 
bp_double_complex},
-#else
-    {'F',       1,              0,              nu_complex_stub, 
np_complex_stub},
-    {'D',       1,              0,              nu_complex_stub, 
np_complex_stub},
-#endif
     {0}
 };
 
@@ -1324,7 +1285,6 @@ lu_double(_structmodulestate *state, const char *p, const 
formatdef *f)
     return unpack_double(p, 1);
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static PyObject *
 lu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
 {
@@ -1354,7 +1314,6 @@ lu_double_complex(_structmodulestate *state, const char 
*p, const formatdef *f)
     }
     return PyComplex_FromDoubles(x, y);
 }
-#endif
 
 static int
 lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1489,7 +1448,6 @@ lp_double(_structmodulestate *state, char *p, PyObject 
*v, const formatdef *f)
     return PyFloat_Pack8(x, p, 1);
 }
 
-#ifdef Py_HAVE_C_COMPLEX
 static int
 lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const 
formatdef *f)
 {
@@ -1520,7 +1478,6 @@ lp_double_complex(_structmodulestate *state, char *p, 
PyObject *v, const formatd
     }
     return PyFloat_Pack8(x.imag, p + 8, 1);
 }
-#endif
 
 static formatdef lilendian_table[] = {
     {'x',       1,              0,              NULL},
@@ -1542,13 +1499,8 @@ static formatdef lilendian_table[] = {
     {'e',       2,              0,              lu_halffloat,   lp_halffloat},
     {'f',       4,              0,              lu_float,       lp_float},
     {'d',       8,              0,              lu_double,      lp_double},
-#ifdef Py_HAVE_C_COMPLEX
     {'F',       8,              0,              lu_float_complex, 
lp_float_complex},
     {'D',       16,             0,              lu_double_complex, 
lp_double_complex},
-#else
-    {'F',       1,              0,              nu_complex_stub, 
np_complex_stub},
-    {'D',       1,              0,              nu_complex_stub, 
np_complex_stub},
-#endif
     {0}
 };
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to