Skip to content

Commit 769cc83

Browse files
gh-148464: Add missing __ctype_le/be__ attributes for complex types in the ctype module (GH-148485)
Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com>
1 parent 2a07ff9 commit 769cc83

File tree

4 files changed

+155
-14
lines changed

4 files changed

+155
-14
lines changed

Lib/test/test_ctypes/test_byteswap.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import binascii
2+
import ctypes
23
import math
34
import struct
45
import sys
@@ -165,6 +166,48 @@ def test_endian_double(self):
165166
self.assertEqual(s.value, math.pi)
166167
self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s))
167168

169+
@unittest.skipUnless(hasattr(ctypes, 'c_float_complex'), "No complex types")
170+
def test_endian_float_complex(self):
171+
c_float_complex = ctypes.c_float_complex
172+
if sys.byteorder == "little":
173+
self.assertIs(c_float_complex.__ctype_le__, c_float_complex)
174+
self.assertIs(c_float_complex.__ctype_be__.__ctype_le__,
175+
c_float_complex)
176+
else:
177+
self.assertIs(c_float_complex.__ctype_be__, c_float_complex)
178+
self.assertIs(c_float_complex.__ctype_le__.__ctype_be__,
179+
c_float_complex)
180+
s = c_float_complex(math.pi+1j)
181+
self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s))
182+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
183+
s = c_float_complex.__ctype_le__(math.pi+1j)
184+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
185+
self.assertEqual(bin(struct.pack("<F", math.pi+1j)), bin(s))
186+
s = c_float_complex.__ctype_be__(math.pi+1j)
187+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
188+
self.assertEqual(bin(struct.pack(">F", math.pi+1j)), bin(s))
189+
190+
@unittest.skipUnless(hasattr(ctypes, 'c_double_complex'), "No complex types")
191+
def test_endian_double_complex(self):
192+
c_double_complex = ctypes.c_double_complex
193+
if sys.byteorder == "little":
194+
self.assertIs(c_double_complex.__ctype_le__, c_double_complex)
195+
self.assertIs(c_double_complex.__ctype_be__.__ctype_le__,
196+
c_double_complex)
197+
else:
198+
self.assertIs(c_double_complex.__ctype_be__, c_double_complex)
199+
self.assertIs(c_double_complex.__ctype_le__.__ctype_be__,
200+
c_double_complex)
201+
s = c_double_complex(math.pi+1j)
202+
self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s))
203+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
204+
s = c_double_complex.__ctype_le__(math.pi+1j)
205+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
206+
self.assertEqual(bin(struct.pack("<D", math.pi+1j)), bin(s))
207+
s = c_double_complex.__ctype_be__(math.pi+1j)
208+
self.assertAlmostEqual(s.value, math.pi+1j, places=6)
209+
self.assertEqual(bin(struct.pack(">D", math.pi+1j)), bin(s))
210+
168211
def test_endian_other(self):
169212
self.assertIs(c_byte.__ctype_le__, c_byte)
170213
self.assertIs(c_byte.__ctype_be__, c_byte)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add missing ``__ctype_le/be__`` attributes for
2+
:class:`~ctypes.c_float_complex` and :class:`~ctypes.c_double_complex`. Patch
3+
by Sergey B Kirpichev.

Modules/_ctypes/_ctypes.c

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,6 +2222,31 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value)
22222222
return NULL;
22232223
}
22242224

2225+
static int
2226+
set_stginfo_ffi_type_pointer(StgInfo *stginfo, struct fielddesc *fmt)
2227+
{
2228+
if (!fmt->pffi_type->elements) {
2229+
stginfo->ffi_type_pointer = *fmt->pffi_type;
2230+
}
2231+
else {
2232+
/* From primitive types - only complex types have the elements
2233+
struct field as non-NULL (two element array). */
2234+
assert(fmt->pffi_type->type == FFI_TYPE_COMPLEX);
2235+
const size_t els_size = 2 * sizeof(ffi_type *);
2236+
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
2237+
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
2238+
stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
2239+
stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
2240+
if (!stginfo->ffi_type_pointer.elements) {
2241+
PyErr_NoMemory();
2242+
return -1;
2243+
}
2244+
memcpy(stginfo->ffi_type_pointer.elements,
2245+
fmt->pffi_type->elements, els_size);
2246+
}
2247+
return 0;
2248+
}
2249+
22252250
static PyMethodDef c_void_p_methods[] = {C_VOID_P_FROM_PARAM_METHODDEF {0}};
22262251
static PyMethodDef c_char_p_methods[] = {C_CHAR_P_FROM_PARAM_METHODDEF {0}};
22272252
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,
22662291
Py_DECREF(result);
22672292
return NULL;
22682293
}
2269-
2270-
stginfo->ffi_type_pointer = *fmt->pffi_type;
2294+
if (set_stginfo_ffi_type_pointer(stginfo, fmt)) {
2295+
Py_DECREF(result);
2296+
return NULL;
2297+
}
22712298
stginfo->align = fmt->pffi_type->alignment;
22722299
stginfo->length = 0;
22732300
stginfo->size = fmt->pffi_type->size;
@@ -2362,18 +2389,8 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
23622389
if (!stginfo) {
23632390
goto error;
23642391
}
2365-
2366-
if (!fmt->pffi_type->elements) {
2367-
stginfo->ffi_type_pointer = *fmt->pffi_type;
2368-
}
2369-
else {
2370-
const size_t els_size = sizeof(fmt->pffi_type->elements);
2371-
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
2372-
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
2373-
stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
2374-
stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
2375-
memcpy(stginfo->ffi_type_pointer.elements,
2376-
fmt->pffi_type->elements, els_size);
2392+
if (set_stginfo_ffi_type_pointer(stginfo, fmt)) {
2393+
goto error;
23772394
}
23782395
stginfo->align = fmt->pffi_type->alignment;
23792396
stginfo->length = 0;

Modules/_ctypes/cfield.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,44 @@ D_get(void *ptr, Py_ssize_t size)
792792
return PyComplex_FromDoubles(x[0], x[1]);
793793
}
794794

795+
static PyObject *
796+
D_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
797+
{
798+
assert(NUM_BITS(size) || (size == 2*sizeof(double)));
799+
Py_complex c = PyComplex_AsCComplex(value);
800+
801+
if (c.real == -1 && PyErr_Occurred()) {
802+
return NULL;
803+
}
804+
#ifdef WORDS_BIGENDIAN
805+
if (PyFloat_Pack8(c.real, ptr, 1)
806+
|| PyFloat_Pack8(c.imag, ptr + sizeof(double), 1))
807+
{
808+
return NULL;
809+
}
810+
#else
811+
if (PyFloat_Pack8(c.real, ptr, 0)
812+
|| PyFloat_Pack8(c.imag, ptr + sizeof(double), 0))
813+
{
814+
return NULL;
815+
}
816+
#endif
817+
_RET(value);
818+
}
819+
820+
static PyObject *
821+
D_get_sw(void *ptr, Py_ssize_t size)
822+
{
823+
assert(NUM_BITS(size) || (size == 2*sizeof(double)));
824+
#ifdef WORDS_BIGENDIAN
825+
return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 1),
826+
PyFloat_Unpack8(ptr + sizeof(double), 1));
827+
#else
828+
return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 0),
829+
PyFloat_Unpack8(ptr + sizeof(double), 0));
830+
#endif
831+
}
832+
795833
/* F: float complex */
796834
static PyObject *
797835
F_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -817,6 +855,44 @@ F_get(void *ptr, Py_ssize_t size)
817855
return PyComplex_FromDoubles(x[0], x[1]);
818856
}
819857

858+
static PyObject *
859+
F_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
860+
{
861+
assert(NUM_BITS(size) || (size == 2*sizeof(float)));
862+
Py_complex c = PyComplex_AsCComplex(value);
863+
864+
if (c.real == -1 && PyErr_Occurred()) {
865+
return NULL;
866+
}
867+
#ifdef WORDS_BIGENDIAN
868+
if (PyFloat_Pack4(c.real, ptr, 1)
869+
|| PyFloat_Pack4(c.imag, ptr + sizeof(float), 1))
870+
{
871+
return NULL;
872+
}
873+
#else
874+
if (PyFloat_Pack4(c.real, ptr, 0)
875+
|| PyFloat_Pack4(c.imag, ptr + sizeof(float), 0))
876+
{
877+
return NULL;
878+
}
879+
#endif
880+
_RET(value);
881+
}
882+
883+
static PyObject *
884+
F_get_sw(void *ptr, Py_ssize_t size)
885+
{
886+
assert(NUM_BITS(size) || (size == 2*sizeof(float)));
887+
#ifdef WORDS_BIGENDIAN
888+
return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 1),
889+
PyFloat_Unpack4(ptr + sizeof(float), 1));
890+
#else
891+
return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 0),
892+
PyFloat_Unpack4(ptr + sizeof(float), 0));
893+
#endif
894+
}
895+
820896
/* G: long double complex */
821897
static PyObject *
822898
G_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -1602,7 +1678,9 @@ for base_code, base_c_type in [
16021678
#if defined(_Py_FFI_SUPPORT_C_COMPLEX)
16031679
if (Py_FFI_COMPLEX_AVAILABLE) {
16041680
TABLE_ENTRY(D, &ffi_type_complex_double);
1681+
TABLE_ENTRY_SW(D, &ffi_type_complex_double);
16051682
TABLE_ENTRY(F, &ffi_type_complex_float);
1683+
TABLE_ENTRY_SW(F, &ffi_type_complex_float);
16061684
TABLE_ENTRY(G, &ffi_type_complex_longdouble);
16071685
}
16081686
#endif

0 commit comments

Comments
 (0)