]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-121249: unconditionally support `complex` types in `struct` (GH-132864)
authorSergey B Kirpichev <skirpichev@gmail.com>
Fri, 2 May 2025 16:24:52 +0000 (19:24 +0300)
committerGitHub <noreply@github.com>
Fri, 2 May 2025 16:24:52 +0000 (18:24 +0200)
Co-authored-by: Lisandro Dalcin <dalcinl@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Doc/library/struct.rst
Doc/whatsnew/3.14.rst
Lib/test/test_struct.py
Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst [new file with mode: 0644]
Modules/_struct.c

index 071b90b9359fa552ecf301703c34a6ad699f527c..66a1fdea54d85780d75aea61cd8e59b519cf91f3 100644 (file)
@@ -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'``.
index 460b77a2385911ced18c5c31f4770da023f112f8..62dd0551483e976011ac70ef69c8aead20f5bb02 100644 (file)
@@ -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`.)
 
 
index a410fd5a1940d12ad3ab95436062b3d72ee0a2c4..7df01f28f09118ea99c82be581d864cbf13ccc53 100644 (file)
@@ -22,12 +22,6 @@ byteorders = '', '@', '=', '<', '>', '!'
 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 @@ class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
         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 @@ class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
                     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 (file)
index 0000000..28140ab
--- /dev/null
@@ -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.
index ee757ed8a9d29d4798f9fb200974e7feaeb8636f..e400f607b558dc8f46afaeded55dbaf2ac488a11 100644 (file)
@@ -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}
 };