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