From c540870a5bd18214e3d26afe609c8473d872ec36 Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Tue, 14 May 2019 11:53:11 -0700 Subject: [PATCH 01/10] bpo-18060: PyCStgDict_clone() needs to account for the length of the base class when computing the length of the derived class. --- Modules/_ctypes/stgdict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 3f8a0316616bf3..acad3aef218f84 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -639,7 +639,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->size = size; stgdict->align = total_align; - stgdict->length = len; /* ADD ffi_ofs? */ + stgdict->length = ffi_ofs + len; /* We did check that this flag was NOT set above, it must not have been set until now. */ From a46b92e2b70d20c92231fdb125af6c1e43d54b29 Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Thu, 16 May 2019 19:54:48 -0700 Subject: [PATCH 02/10] bpo-18060: use the size of the _fields_ elements array in the parent class of a Srtructure or Union derived subclass when cloning or modifying the _fields_ array. The old behavior of using the number of elements in the parent class can cause the array to be truncated when it is copied, especially when there are multiple layers of subclassing. Tests are included. --- Lib/ctypes/test/test_structures.py | 80 ++++++++++++++++++++++++++++++ Modules/_ctypes/stgdict.c | 27 +++++++--- 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index d1ea43bc7e3b6f..40f55924732601 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -3,7 +3,9 @@ from ctypes.test import need_symbol from struct import calcsize import _ctypes_test +from ctypes.util import find_library import test.support +import pdb class SubclassesTest(unittest.TestCase): def test_subclass(self): @@ -438,6 +440,84 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) + def test_issue18060_a(self): + # The call to atan2() should succeed if the + # class fields were correctly cloned in the + # subclasses. Otherwise, it will segfault. + # + # This test case calls + # PyCStructUnionType_update_stgdict() for each + # _fields_ assignment, and PyCStgDict_clone() + # for the Mid and Vector class definitions. + libm = CDLL(find_library('m')) + + class Base(Structure): + _fields_ = [('y', c_double), + ('x', c_double)] + + class Mid(Base): + pass + + Mid._fields_ = [] + + class Vector(Mid): pass + + libm.atan2.argtypes = [Vector] + libm.atan2.restype = c_double + + arg = Vector(y=0.0, x=-1.0) + self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + + def test_issue18060_b(self): + # The call to atan2() should succeed if the + # class fields were correctly cloned in the + # subclasses. Otherwise, it will segfault. + # + # This test case calls + # PyCStructUnionType_update_stgdict() for each + # _fields_ assignment. + libm = CDLL(find_library('m')) + + class Base(Structure): + _fields_ = [('y', c_double), + ('x', c_double)] + + class Mid(Base): + _fields_ = [] + + class Vector(Mid): + _fields_ = [] + + libm.atan2.argtypes = [Vector] + libm.atan2.restype = c_double + + arg = Vector(y=0.0, x=-1.0) + self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + + def test_issue18060_c(self): + # The call to atan2() should succeed if the + # class fields were correctly cloned in the + # subclasses. Otherwise, it will segfault. + # + # This test case calls + # PyCStructUnionType_update_stgdict() for each + # _fields_ assignment. + libm = CDLL(find_library('m')) + + class Base(Structure): + _fields_ = [('y', c_double)] + + class Mid(Base): + _fields_ = [] + + class Vector(Mid): + _fields_ = [('x', c_double)] + + libm.atan2.argtypes = [Vector] + libm.atan2.restype = c_double + + arg = Vector(y=0.0, x=-1.0) + self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) class PointerMemberTestCase(unittest.TestCase): diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index acad3aef218f84..068a63bc13de3c 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -109,7 +109,14 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) if (src->ffi_type_pointer.elements == NULL) return 0; - size = sizeof(ffi_type *) * (src->length + 1); + /* src->ffi_type_pointer.elements is an */ + /* array of pointers to ffi_type that */ + /* contains a trailing NULL pointer. */ + /* src->size is the size of the array */ + /* in bytes excluding the trailing NULL */ + /* pointer, so we have to add it in to */ + /* to compute how many bytes to copy. */ + size = src->size + sizeof(ffi_type *); dst->ffi_type_pointer.elements = PyMem_Malloc(size); if (dst->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); @@ -341,7 +348,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct _Py_IDENTIFIER(_use_broken_old_ctypes_structure_semantics_); _Py_IDENTIFIER(_pack_); StgDictObject *stgdict, *basedict; - Py_ssize_t len, offset, size, align, i; + Py_ssize_t len, offset, size, align, i, base_len; Py_ssize_t union_size, total_align; Py_ssize_t field_size = 0; int bitofs; @@ -445,19 +452,23 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct union_size = 0; total_align = align ? align : 1; stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1); + base_len = size / sizeof(ffi_type *); /* calculate the cumulative number */ + /* of elements in the base class */ + /* so that all elements get copied */ + /* to the child class */ + stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, base_len + len + 1); if (stgdict->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } memset(stgdict->ffi_type_pointer.elements, 0, - sizeof(ffi_type *) * (basedict->length + len + 1)); - if (basedict->length > 0) { + sizeof(ffi_type *) * (base_len + len + 1)); + if (size > 0) { memcpy(stgdict->ffi_type_pointer.elements, basedict->ffi_type_pointer.elements, - sizeof(ffi_type *) * (basedict->length)); + size); } - ffi_ofs = basedict->length; + ffi_ofs = base_len; /* index of the child class's first element */ } else { offset = 0; size = 0; @@ -639,7 +650,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->size = size; stgdict->align = total_align; - stgdict->length = ffi_ofs + len; + stgdict->length = len; /* the number of elements in the child class */ /* We did check that this flag was NOT set above, it must not have been set until now. */ From 0151db96d2d41bd1f83f705a4d1cd6f44f2893bb Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Thu, 16 May 2019 21:23:58 -0700 Subject: [PATCH 03/10] bpo-18060: remove extraneous debug code --- Lib/ctypes/test/test_structures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index 40f55924732601..cd4c868f3e0bae 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -5,7 +5,6 @@ import _ctypes_test from ctypes.util import find_library import test.support -import pdb class SubclassesTest(unittest.TestCase): def test_subclass(self): From 5c0bc2c5880622a511a72a13c4662b11ad1025f1 Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Thu, 16 May 2019 23:45:34 -0700 Subject: [PATCH 04/10] bpo-18060: disable tests on Win32 platforms --- Lib/ctypes/test/test_structures.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index cd4c868f3e0bae..e74e02488af63a 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -5,6 +5,7 @@ import _ctypes_test from ctypes.util import find_library import test.support +import sys class SubclassesTest(unittest.TestCase): def test_subclass(self): @@ -439,6 +440,7 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_issue18060_a(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the @@ -467,6 +469,7 @@ class Vector(Mid): pass arg = Vector(y=0.0, x=-1.0) self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_issue18060_b(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the @@ -493,6 +496,7 @@ class Vector(Mid): arg = Vector(y=0.0, x=-1.0) self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_issue18060_c(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the From 925fbb8212fce7ef8c02883063221997f2f27409 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" Date: Fri, 17 May 2019 07:22:34 +0000 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst diff --git a/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst new file mode 100644 index 00000000000000..3fa61dfab5db85 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst @@ -0,0 +1 @@ +Fixed a class inheritance issue that can cause segfaults when deriving two or more levels of subclasses from a base class of Structure or Union. \ No newline at end of file From bc6c7bb084a7132ef03524741860f276b9420151 Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Sat, 13 Jul 2019 19:35:17 -0700 Subject: [PATCH 06/10] bpo-18060: enable win32 tests --- Lib/ctypes/test/test_structures.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index e74e02488af63a..dc6a01e8b33e9b 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -6,6 +6,7 @@ from ctypes.util import find_library import test.support import sys +from platform import architecture as _architecture class SubclassesTest(unittest.TestCase): def test_subclass(self): @@ -440,7 +441,7 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") def test_issue18060_a(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the @@ -450,7 +451,10 @@ def test_issue18060_a(self): # PyCStructUnionType_update_stgdict() for each # _fields_ assignment, and PyCStgDict_clone() # for the Mid and Vector class definitions. - libm = CDLL(find_library('m')) + if sys.platform == 'win32': + libm = CDLL(find_library('msvcrt.dll')) + else: + libm = CDLL(find_library('m')) class Base(Structure): _fields_ = [('y', c_double), @@ -469,7 +473,7 @@ class Vector(Mid): pass arg = Vector(y=0.0, x=-1.0) self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") def test_issue18060_b(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the @@ -478,7 +482,10 @@ def test_issue18060_b(self): # This test case calls # PyCStructUnionType_update_stgdict() for each # _fields_ assignment. - libm = CDLL(find_library('m')) + if sys.platform == 'win32': + libm = CDLL(find_library('msvcrt.dll')) + else: + libm = CDLL(find_library('m')) class Base(Structure): _fields_ = [('y', c_double), @@ -496,7 +503,7 @@ class Vector(Mid): arg = Vector(y=0.0, x=-1.0) self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") def test_issue18060_c(self): # The call to atan2() should succeed if the # class fields were correctly cloned in the @@ -505,7 +512,10 @@ def test_issue18060_c(self): # This test case calls # PyCStructUnionType_update_stgdict() for each # _fields_ assignment. - libm = CDLL(find_library('m')) + if sys.platform == 'win32': + libm = CDLL(find_library('msvcrt.dll')) + else: + libm = CDLL(find_library('m')) class Base(Structure): _fields_ = [('y', c_double)] From 6328bc84e515e85d662bb2d76d4a6e1c0ba9e99d Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Sun, 14 Jul 2019 00:07:25 -0700 Subject: [PATCH 07/10] bpo-18060: take alignment into account when calculating number of elements in parent class --- Modules/_ctypes/stgdict.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 068a63bc13de3c..ee9d6ff281e32e 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -116,7 +116,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) /* in bytes excluding the trailing NULL */ /* pointer, so we have to add it in to */ /* to compute how many bytes to copy. */ - size = src->size + sizeof(ffi_type *); + size = src->size + max(sizeof(ffi_type *), src->align); dst->ffi_type_pointer.elements = PyMem_Malloc(size); if (dst->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); @@ -452,10 +452,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct union_size = 0; total_align = align ? align : 1; stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - base_len = size / sizeof(ffi_type *); /* calculate the cumulative number */ - /* of elements in the base class */ - /* so that all elements get copied */ - /* to the child class */ + /* calculate the cumulative number of elements in the base class */ + /* so that all elements get copied to the child class */ + base_len = size / max(sizeof(ffi_type *), align); stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, base_len + len + 1); if (stgdict->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); From f7c01a1565b8828cd9a4ac7a8a4338f3852e7b9a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Dec 2023 14:09:23 +0200 Subject: [PATCH 08/10] Add a newline. --- .../next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst index 3fa61dfab5db85..3fefbc3efb63c0 100644 --- a/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst +++ b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst @@ -1 +1,2 @@ -Fixed a class inheritance issue that can cause segfaults when deriving two or more levels of subclasses from a base class of Structure or Union. \ No newline at end of file +Fixed a class inheritance issue that can cause segfaults when deriving two or more levels of subclasses from a base class of Structure or Union. + From e2947577c0b3882a59db4f253510f1fe9b877e8c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Dec 2023 19:50:36 +0200 Subject: [PATCH 09/10] Correctly determine the size of StgDictObject.ffi_type_pointer.elements. Now StgDictObject.length of Structure and Union is the number of all fields, including fields of superclases. --- Modules/_ctypes/_ctypes.c | 10 +++++----- Modules/_ctypes/stgdict.c | 26 ++++++++------------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f909a9496b6526..fc16b9176fd1c0 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4354,10 +4354,10 @@ _init_pos_args(PyObject *self, PyTypeObject *type, return index; } - for (i = 0; - i < dict->length && (i+index) < PyTuple_GET_SIZE(args); + for (i = index; + i < dict->length && i < PyTuple_GET_SIZE(args); ++i) { - PyObject *pair = PySequence_GetItem(fields, i); + PyObject *pair = PySequence_GetItem(fields, i - index); PyObject *name, *val; int res; if (!pair) @@ -4367,7 +4367,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, Py_DECREF(pair); return -1; } - val = PyTuple_GET_ITEM(args, i + index); + val = PyTuple_GET_ITEM(args, i); if (kwds) { res = PyDict_Contains(kwds, name); if (res != 0) { @@ -4388,7 +4388,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, if (res == -1) return -1; } - return index + dict->length; + return dict->length; } static int diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 9b03209f88f174..fb3e20e8db3e27 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -119,14 +119,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) if (src->ffi_type_pointer.elements == NULL) return 0; - /* src->ffi_type_pointer.elements is an */ - /* array of pointers to ffi_type that */ - /* contains a trailing NULL pointer. */ - /* src->size is the size of the array */ - /* in bytes excluding the trailing NULL */ - /* pointer, so we have to add it in to */ - /* to compute how many bytes to copy. */ - size = src->size + max(sizeof(ffi_type *), src->align); + size = sizeof(ffi_type *) * (src->length + 1); dst->ffi_type_pointer.elements = PyMem_Malloc(size); if (dst->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); @@ -380,7 +373,7 @@ int PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) { StgDictObject *stgdict, *basedict; - Py_ssize_t len, offset, size, align, i, base_len; + Py_ssize_t len, offset, size, align, i; Py_ssize_t union_size, total_align, aligned_size; Py_ssize_t field_size = 0; int bitofs; @@ -472,22 +465,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct union_size = 0; total_align = align ? align : 1; stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - /* calculate the cumulative number of elements in the base class */ - /* so that all elements get copied to the child class */ - base_len = size / max(sizeof(ffi_type *), align); - stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, base_len + len + 1); + stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1); if (stgdict->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } memset(stgdict->ffi_type_pointer.elements, 0, - sizeof(ffi_type *) * (base_len + len + 1)); - if (size > 0) { + sizeof(ffi_type *) * (basedict->length + len + 1)); + if (basedict->length > 0) { memcpy(stgdict->ffi_type_pointer.elements, basedict->ffi_type_pointer.elements, - size); + sizeof(ffi_type *) * (basedict->length)); } - ffi_ofs = base_len; /* index of the child class's first element */ + ffi_ofs = basedict->length; } else { offset = 0; size = 0; @@ -705,7 +695,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->size = aligned_size; stgdict->align = total_align; - stgdict->length = len; /* the number of elements in the child class */ + stgdict->length = ffi_ofs + len; /* * The value of MAX_STRUCT_SIZE depends on the platform Python is running on. From cedea96b6eeb869663c0bd806bd4f1b3d7e30753 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 28 Dec 2023 11:21:27 +0200 Subject: [PATCH 10/10] Refactor tests and skip them on big-endian platforms. --- Lib/test/test_ctypes/test_structures.py | 69 +++++++------------------ 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index 0526e6076412e7..3eafc77ca70aea 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -473,96 +473,65 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) - @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") - def test_issue18060_a(self): + def _test_issue18060(self, Vector): # The call to atan2() should succeed if the # class fields were correctly cloned in the # subclasses. Otherwise, it will segfault. - # - # This test case calls - # PyCStructUnionType_update_stgdict() for each - # _fields_ assignment, and PyCStgDict_clone() - # for the Mid and Vector class definitions. if sys.platform == 'win32': libm = CDLL(find_library('msvcrt.dll')) else: libm = CDLL(find_library('m')) + libm.atan2.argtypes = [Vector] + libm.atan2.restype = c_double + + arg = Vector(y=0.0, x=-1.0) + self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + + @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") + @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") + def test_issue18060_a(self): + # This test case calls + # PyCStructUnionType_update_stgdict() for each + # _fields_ assignment, and PyCStgDict_clone() + # for the Mid and Vector class definitions. class Base(Structure): _fields_ = [('y', c_double), ('x', c_double)] - class Mid(Base): pass - Mid._fields_ = [] - class Vector(Mid): pass - - libm.atan2.argtypes = [Vector] - libm.atan2.restype = c_double - - arg = Vector(y=0.0, x=-1.0) - self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + self._test_issue18060(Vector) @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") + @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") def test_issue18060_b(self): - # The call to atan2() should succeed if the - # class fields were correctly cloned in the - # subclasses. Otherwise, it will segfault. - # # This test case calls # PyCStructUnionType_update_stgdict() for each # _fields_ assignment. - if sys.platform == 'win32': - libm = CDLL(find_library('msvcrt.dll')) - else: - libm = CDLL(find_library('m')) - class Base(Structure): _fields_ = [('y', c_double), ('x', c_double)] - class Mid(Base): _fields_ = [] - class Vector(Mid): _fields_ = [] - - libm.atan2.argtypes = [Vector] - libm.atan2.restype = c_double - - arg = Vector(y=0.0, x=-1.0) - self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + self._test_issue18060(Vector) @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build") + @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") def test_issue18060_c(self): - # The call to atan2() should succeed if the - # class fields were correctly cloned in the - # subclasses. Otherwise, it will segfault. - # # This test case calls # PyCStructUnionType_update_stgdict() for each # _fields_ assignment. - if sys.platform == 'win32': - libm = CDLL(find_library('msvcrt.dll')) - else: - libm = CDLL(find_library('m')) - class Base(Structure): _fields_ = [('y', c_double)] - class Mid(Base): _fields_ = [] - class Vector(Mid): _fields_ = [('x', c_double)] - - libm.atan2.argtypes = [Vector] - libm.atan2.restype = c_double - - arg = Vector(y=0.0, x=-1.0) - self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793) + self._test_issue18060(Vector) def test_array_in_struct(self): # See bpo-22273