]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148464: Add missing ``__ctype_le/be__`` attributes for complex types in the ctype...
authorSergey B Kirpichev <skirpichev@gmail.com>
Fri, 17 Apr 2026 12:09:09 +0000 (15:09 +0300)
committerGitHub <noreply@github.com>
Fri, 17 Apr 2026 12:09:09 +0000 (14:09 +0200)
Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com>
Lib/test/test_ctypes/test_byteswap.py
Misc/NEWS.d/next/Library/2026-04-13-06-22-27.gh-issue-148464.Bj_NZy.rst [new file with mode: 0644]
Modules/_ctypes/_ctypes.c
Modules/_ctypes/cfield.c

index f14e1aa32e17ab78dfade449e237f1c92def0ce3..6a1bae14773d2775ddb52194dc485e277baa5765 100644 (file)
@@ -1,4 +1,5 @@
 import binascii
+import ctypes
 import math
 import struct
 import sys
@@ -165,6 +166,48 @@ class Test(unittest.TestCase, StructCheckMixin):
         self.assertEqual(s.value, math.pi)
         self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s))
 
+    @unittest.skipUnless(hasattr(ctypes, 'c_float_complex'), "No complex types")
+    def test_endian_float_complex(self):
+        c_float_complex = ctypes.c_float_complex
+        if sys.byteorder == "little":
+            self.assertIs(c_float_complex.__ctype_le__, c_float_complex)
+            self.assertIs(c_float_complex.__ctype_be__.__ctype_le__,
+                          c_float_complex)
+        else:
+            self.assertIs(c_float_complex.__ctype_be__, c_float_complex)
+            self.assertIs(c_float_complex.__ctype_le__.__ctype_be__,
+                          c_float_complex)
+        s = c_float_complex(math.pi+1j)
+        self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s))
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        s = c_float_complex.__ctype_le__(math.pi+1j)
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        self.assertEqual(bin(struct.pack("<F", math.pi+1j)), bin(s))
+        s = c_float_complex.__ctype_be__(math.pi+1j)
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        self.assertEqual(bin(struct.pack(">F", math.pi+1j)), bin(s))
+
+    @unittest.skipUnless(hasattr(ctypes, 'c_double_complex'), "No complex types")
+    def test_endian_double_complex(self):
+        c_double_complex = ctypes.c_double_complex
+        if sys.byteorder == "little":
+            self.assertIs(c_double_complex.__ctype_le__, c_double_complex)
+            self.assertIs(c_double_complex.__ctype_be__.__ctype_le__,
+                          c_double_complex)
+        else:
+            self.assertIs(c_double_complex.__ctype_be__, c_double_complex)
+            self.assertIs(c_double_complex.__ctype_le__.__ctype_be__,
+                          c_double_complex)
+        s = c_double_complex(math.pi+1j)
+        self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s))
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        s = c_double_complex.__ctype_le__(math.pi+1j)
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        self.assertEqual(bin(struct.pack("<D", math.pi+1j)), bin(s))
+        s = c_double_complex.__ctype_be__(math.pi+1j)
+        self.assertAlmostEqual(s.value, math.pi+1j, places=6)
+        self.assertEqual(bin(struct.pack(">D", math.pi+1j)), bin(s))
+
     def test_endian_other(self):
         self.assertIs(c_byte.__ctype_le__, c_byte)
         self.assertIs(c_byte.__ctype_be__, c_byte)
diff --git a/Misc/NEWS.d/next/Library/2026-04-13-06-22-27.gh-issue-148464.Bj_NZy.rst b/Misc/NEWS.d/next/Library/2026-04-13-06-22-27.gh-issue-148464.Bj_NZy.rst
new file mode 100644 (file)
index 0000000..85b9953
--- /dev/null
@@ -0,0 +1,3 @@
+Add missing ``__ctype_le/be__`` attributes for
+:class:`~ctypes.c_float_complex` and :class:`~ctypes.c_double_complex`. Patch
+by Sergey B Kirpichev.
index 55eade1c8307ead94f8c68446a409d448aa27bad..0bdc30a0cb3836e2ea2df8fc2e7edd08ab8b57ce 100644 (file)
@@ -2222,6 +2222,31 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value)
     return NULL;
 }
 
+static int
+set_stginfo_ffi_type_pointer(StgInfo *stginfo, struct fielddesc *fmt)
+{
+    if (!fmt->pffi_type->elements) {
+        stginfo->ffi_type_pointer = *fmt->pffi_type;
+    }
+    else {
+        /* From primitive types - only complex types have the elements
+           struct field as non-NULL (two element array). */
+        assert(fmt->pffi_type->type == FFI_TYPE_COMPLEX);
+        const size_t els_size = 2 * sizeof(ffi_type *);
+        stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
+        stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
+        stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
+        stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
+        if (!stginfo->ffi_type_pointer.elements) {
+            PyErr_NoMemory();
+            return -1;
+        }
+        memcpy(stginfo->ffi_type_pointer.elements,
+               fmt->pffi_type->elements, els_size);
+    }
+    return 0;
+}
+
 static PyMethodDef c_void_p_methods[] = {C_VOID_P_FROM_PARAM_METHODDEF {0}};
 static PyMethodDef c_char_p_methods[] = {C_CHAR_P_FROM_PARAM_METHODDEF {0}};
 static PyMethodDef c_wchar_p_methods[] = {C_WCHAR_P_FROM_PARAM_METHODDEF {0}};
@@ -2266,8 +2291,10 @@ static PyObject *CreateSwappedType(ctypes_state *st, PyTypeObject *type,
         Py_DECREF(result);
         return NULL;
     }
-
-    stginfo->ffi_type_pointer = *fmt->pffi_type;
+    if (set_stginfo_ffi_type_pointer(stginfo, fmt)) {
+        Py_DECREF(result);
+        return NULL;
+    }
     stginfo->align = fmt->pffi_type->alignment;
     stginfo->length = 0;
     stginfo->size = fmt->pffi_type->size;
@@ -2362,18 +2389,8 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
     if (!stginfo) {
         goto error;
     }
-
-    if (!fmt->pffi_type->elements) {
-        stginfo->ffi_type_pointer = *fmt->pffi_type;
-    }
-    else {
-        const size_t els_size = sizeof(fmt->pffi_type->elements);
-        stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
-        stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
-        stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
-        stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
-        memcpy(stginfo->ffi_type_pointer.elements,
-               fmt->pffi_type->elements, els_size);
+    if (set_stginfo_ffi_type_pointer(stginfo, fmt)) {
+        goto error;
     }
     stginfo->align = fmt->pffi_type->alignment;
     stginfo->length = 0;
index 4ebca0e0b3db0a332462858b8afedaefa35f234b..b0dc11fdddcea15dbc18ba83c648923b3c57017e 100644 (file)
@@ -792,6 +792,44 @@ D_get(void *ptr, Py_ssize_t size)
     return PyComplex_FromDoubles(x[0], x[1]);
 }
 
+static PyObject *
+D_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
+{
+    assert(NUM_BITS(size) || (size == 2*sizeof(double)));
+    Py_complex c = PyComplex_AsCComplex(value);
+
+    if (c.real == -1 && PyErr_Occurred()) {
+        return NULL;
+    }
+#ifdef WORDS_BIGENDIAN
+    if (PyFloat_Pack8(c.real, ptr, 1)
+        || PyFloat_Pack8(c.imag, ptr + sizeof(double), 1))
+    {
+        return NULL;
+    }
+#else
+    if (PyFloat_Pack8(c.real, ptr, 0)
+        || PyFloat_Pack8(c.imag, ptr + sizeof(double), 0))
+    {
+        return NULL;
+    }
+#endif
+    _RET(value);
+}
+
+static PyObject *
+D_get_sw(void *ptr, Py_ssize_t size)
+{
+    assert(NUM_BITS(size) || (size == 2*sizeof(double)));
+#ifdef WORDS_BIGENDIAN
+    return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 1),
+                                 PyFloat_Unpack8(ptr + sizeof(double), 1));
+#else
+    return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 0),
+                                 PyFloat_Unpack8(ptr + sizeof(double), 0));
+#endif
+}
+
 /* F: float complex */
 static PyObject *
 F_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -817,6 +855,44 @@ F_get(void *ptr, Py_ssize_t size)
     return PyComplex_FromDoubles(x[0], x[1]);
 }
 
+static PyObject *
+F_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
+{
+    assert(NUM_BITS(size) || (size == 2*sizeof(float)));
+    Py_complex c = PyComplex_AsCComplex(value);
+
+    if (c.real == -1 && PyErr_Occurred()) {
+        return NULL;
+    }
+#ifdef WORDS_BIGENDIAN
+    if (PyFloat_Pack4(c.real, ptr, 1)
+        || PyFloat_Pack4(c.imag, ptr + sizeof(float), 1))
+    {
+        return NULL;
+    }
+#else
+    if (PyFloat_Pack4(c.real, ptr, 0)
+        || PyFloat_Pack4(c.imag, ptr + sizeof(float), 0))
+    {
+        return NULL;
+    }
+#endif
+    _RET(value);
+}
+
+static PyObject *
+F_get_sw(void *ptr, Py_ssize_t size)
+{
+    assert(NUM_BITS(size) || (size == 2*sizeof(float)));
+#ifdef WORDS_BIGENDIAN
+    return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 1),
+                                 PyFloat_Unpack4(ptr + sizeof(float), 1));
+#else
+    return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 0),
+                                 PyFloat_Unpack4(ptr + sizeof(float), 0));
+#endif
+}
+
 /* G: long double complex */
 static PyObject *
 G_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -1602,7 +1678,9 @@ for base_code, base_c_type in [
 #if defined(_Py_FFI_SUPPORT_C_COMPLEX)
     if (Py_FFI_COMPLEX_AVAILABLE) {
         TABLE_ENTRY(D, &ffi_type_complex_double);
+        TABLE_ENTRY_SW(D, &ffi_type_complex_double);
         TABLE_ENTRY(F, &ffi_type_complex_float);
+        TABLE_ENTRY_SW(F, &ffi_type_complex_float);
         TABLE_ENTRY(G, &ffi_type_complex_longdouble);
     }
 #endif