mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-124570: ctypes: Run some Structure tests on Union as well (GH-124976)
- Move some Structure tests to test_structunion; use a common base test class + two subclasses to run them on Union too - Remove test_union for now as it's redundant Note: `test_simple_structs` & `test_simple_unions` are in the common file because they share `formats`.
This commit is contained in:
parent
c914212474
commit
01fc3b34cc
3 changed files with 368 additions and 357 deletions
353
Lib/test/test_ctypes/test_structunion.py
Normal file
353
Lib/test/test_ctypes/test_structunion.py
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
"""Common tests for ctypes.Structure and ctypes.Union"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from ctypes import (Structure, Union, POINTER, sizeof, alignment,
|
||||||
|
c_char, c_byte, c_ubyte,
|
||||||
|
c_short, c_ushort, c_int, c_uint,
|
||||||
|
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
|
||||||
|
from ._support import (_CData, PyCStructType, UnionType,
|
||||||
|
Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||||
|
Py_TPFLAGS_IMMUTABLETYPE)
|
||||||
|
from struct import calcsize
|
||||||
|
|
||||||
|
|
||||||
|
class StructUnionTestBase:
|
||||||
|
formats = {"c": c_char,
|
||||||
|
"b": c_byte,
|
||||||
|
"B": c_ubyte,
|
||||||
|
"h": c_short,
|
||||||
|
"H": c_ushort,
|
||||||
|
"i": c_int,
|
||||||
|
"I": c_uint,
|
||||||
|
"l": c_long,
|
||||||
|
"L": c_ulong,
|
||||||
|
"q": c_longlong,
|
||||||
|
"Q": c_ulonglong,
|
||||||
|
"f": c_float,
|
||||||
|
"d": c_double,
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_subclass(self):
|
||||||
|
class X(self.cls):
|
||||||
|
_fields_ = [("a", c_int)]
|
||||||
|
|
||||||
|
class Y(X):
|
||||||
|
_fields_ = [("b", c_int)]
|
||||||
|
|
||||||
|
class Z(X):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||||
|
self.check_sizeof(Y,
|
||||||
|
struct_size=sizeof(c_int)*2,
|
||||||
|
union_size=sizeof(c_int))
|
||||||
|
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||||
|
self.assertEqual(X._fields_, [("a", c_int)])
|
||||||
|
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||||
|
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||||
|
|
||||||
|
def test_subclass_delayed(self):
|
||||||
|
class X(self.cls):
|
||||||
|
pass
|
||||||
|
self.assertEqual(sizeof(X), 0)
|
||||||
|
X._fields_ = [("a", c_int)]
|
||||||
|
|
||||||
|
class Y(X):
|
||||||
|
pass
|
||||||
|
self.assertEqual(sizeof(Y), sizeof(X))
|
||||||
|
Y._fields_ = [("b", c_int)]
|
||||||
|
|
||||||
|
class Z(X):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||||
|
self.check_sizeof(Y,
|
||||||
|
struct_size=sizeof(c_int)*2,
|
||||||
|
union_size=sizeof(c_int))
|
||||||
|
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||||
|
self.assertEqual(X._fields_, [("a", c_int)])
|
||||||
|
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||||
|
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||||
|
|
||||||
|
def test_inheritance_hierarchy(self):
|
||||||
|
self.assertEqual(self.cls.mro(), [self.cls, _CData, object])
|
||||||
|
self.assertEqual(type(self.metacls), type)
|
||||||
|
|
||||||
|
def test_type_flags(self):
|
||||||
|
for cls in self.cls, self.metacls:
|
||||||
|
with self.subTest(cls=cls):
|
||||||
|
self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
||||||
|
self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
||||||
|
|
||||||
|
def test_metaclass_details(self):
|
||||||
|
# Abstract classes (whose metaclass __init__ was not called) can't be
|
||||||
|
# instantiated directly
|
||||||
|
NewClass = self.metacls.__new__(self.metacls, 'NewClass',
|
||||||
|
(self.cls,), {})
|
||||||
|
for cls in self.cls, NewClass:
|
||||||
|
with self.subTest(cls=cls):
|
||||||
|
with self.assertRaisesRegex(TypeError, "abstract class"):
|
||||||
|
obj = cls()
|
||||||
|
|
||||||
|
# Cannot call the metaclass __init__ more than once
|
||||||
|
class T(self.cls):
|
||||||
|
_fields_ = [("x", c_char),
|
||||||
|
("y", c_char)]
|
||||||
|
with self.assertRaisesRegex(SystemError, "already initialized"):
|
||||||
|
self.metacls.__init__(T, 'ptr', (), {})
|
||||||
|
|
||||||
|
def test_alignment(self):
|
||||||
|
class X(self.cls):
|
||||||
|
_fields_ = [("x", c_char * 3)]
|
||||||
|
self.assertEqual(alignment(X), calcsize("s"))
|
||||||
|
self.assertEqual(sizeof(X), calcsize("3s"))
|
||||||
|
|
||||||
|
class Y(self.cls):
|
||||||
|
_fields_ = [("x", c_char * 3),
|
||||||
|
("y", c_int)]
|
||||||
|
self.assertEqual(alignment(Y), alignment(c_int))
|
||||||
|
self.check_sizeof(Y,
|
||||||
|
struct_size=calcsize("3s i"),
|
||||||
|
union_size=max(calcsize("3s"), calcsize("i")))
|
||||||
|
|
||||||
|
class SI(self.cls):
|
||||||
|
_fields_ = [("a", X),
|
||||||
|
("b", Y)]
|
||||||
|
self.assertEqual(alignment(SI), max(alignment(Y), alignment(X)))
|
||||||
|
self.check_sizeof(SI,
|
||||||
|
struct_size=calcsize("3s0i 3si 0i"),
|
||||||
|
union_size=max(calcsize("3s"), calcsize("i")))
|
||||||
|
|
||||||
|
class IS(self.cls):
|
||||||
|
_fields_ = [("b", Y),
|
||||||
|
("a", X)]
|
||||||
|
|
||||||
|
self.assertEqual(alignment(SI), max(alignment(X), alignment(Y)))
|
||||||
|
self.check_sizeof(IS,
|
||||||
|
struct_size=calcsize("3si 3s 0i"),
|
||||||
|
union_size=max(calcsize("3s"), calcsize("i")))
|
||||||
|
|
||||||
|
class XX(self.cls):
|
||||||
|
_fields_ = [("a", X),
|
||||||
|
("b", X)]
|
||||||
|
self.assertEqual(alignment(XX), alignment(X))
|
||||||
|
self.check_sizeof(XX,
|
||||||
|
struct_size=calcsize("3s 3s 0s"),
|
||||||
|
union_size=calcsize("3s"))
|
||||||
|
|
||||||
|
def test_empty(self):
|
||||||
|
# I had problems with these
|
||||||
|
#
|
||||||
|
# Although these are pathological cases: Empty Structures!
|
||||||
|
class X(self.cls):
|
||||||
|
_fields_ = []
|
||||||
|
|
||||||
|
# Is this really the correct alignment, or should it be 0?
|
||||||
|
self.assertTrue(alignment(X) == 1)
|
||||||
|
self.assertTrue(sizeof(X) == 0)
|
||||||
|
|
||||||
|
class XX(self.cls):
|
||||||
|
_fields_ = [("a", X),
|
||||||
|
("b", X)]
|
||||||
|
|
||||||
|
self.assertEqual(alignment(XX), 1)
|
||||||
|
self.assertEqual(sizeof(XX), 0)
|
||||||
|
|
||||||
|
def test_fields(self):
|
||||||
|
# test the offset and size attributes of Structure/Union fields.
|
||||||
|
class X(self.cls):
|
||||||
|
_fields_ = [("x", c_int),
|
||||||
|
("y", c_char)]
|
||||||
|
|
||||||
|
self.assertEqual(X.x.offset, 0)
|
||||||
|
self.assertEqual(X.x.size, sizeof(c_int))
|
||||||
|
|
||||||
|
if self.cls == Structure:
|
||||||
|
self.assertEqual(X.y.offset, sizeof(c_int))
|
||||||
|
else:
|
||||||
|
self.assertEqual(X.y.offset, 0)
|
||||||
|
self.assertEqual(X.y.size, sizeof(c_char))
|
||||||
|
|
||||||
|
# readonly
|
||||||
|
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
||||||
|
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
||||||
|
|
||||||
|
# XXX Should we check nested data types also?
|
||||||
|
# offset is always relative to the class...
|
||||||
|
|
||||||
|
def test_invalid_field_types(self):
|
||||||
|
class POINT(self.cls):
|
||||||
|
pass
|
||||||
|
self.assertRaises(TypeError, setattr, POINT, "_fields_", [("x", 1), ("y", 2)])
|
||||||
|
|
||||||
|
def test_invalid_name(self):
|
||||||
|
# field name must be string
|
||||||
|
def declare_with_name(name):
|
||||||
|
class S(self.cls):
|
||||||
|
_fields_ = [(name, c_int)]
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, declare_with_name, b"x")
|
||||||
|
|
||||||
|
def test_intarray_fields(self):
|
||||||
|
class SomeInts(self.cls):
|
||||||
|
_fields_ = [("a", c_int * 4)]
|
||||||
|
|
||||||
|
# can use tuple to initialize array (but not list!)
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0])
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0])
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[::-1], [0, 0, 2, 1])
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[::2], [1, 0])
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[1:5:6], [2])
|
||||||
|
self.assertEqual(SomeInts((1, 2)).a[6:4:-1], [])
|
||||||
|
self.assertEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4])
|
||||||
|
self.assertEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4])
|
||||||
|
# too long
|
||||||
|
# XXX Should raise ValueError?, not RuntimeError
|
||||||
|
self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5))
|
||||||
|
|
||||||
|
def test_huge_field_name(self):
|
||||||
|
# issue12881: segfault with large structure field names
|
||||||
|
def create_class(length):
|
||||||
|
class S(self.cls):
|
||||||
|
_fields_ = [('x' * length, c_int)]
|
||||||
|
|
||||||
|
for length in [10 ** i for i in range(0, 8)]:
|
||||||
|
try:
|
||||||
|
create_class(length)
|
||||||
|
except MemoryError:
|
||||||
|
# MemoryErrors are OK, we just don't want to segfault
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_abstract_class(self):
|
||||||
|
class X(self.cls):
|
||||||
|
_abstract_ = "something"
|
||||||
|
with self.assertRaisesRegex(TypeError, r"^abstract class$"):
|
||||||
|
X()
|
||||||
|
|
||||||
|
def test_methods(self):
|
||||||
|
self.assertIn("in_dll", dir(type(self.cls)))
|
||||||
|
self.assertIn("from_address", dir(type(self.cls)))
|
||||||
|
self.assertIn("in_dll", dir(type(self.cls)))
|
||||||
|
|
||||||
|
|
||||||
|
class StructureTestCase(unittest.TestCase, StructUnionTestBase):
|
||||||
|
cls = Structure
|
||||||
|
metacls = PyCStructType
|
||||||
|
|
||||||
|
def test_metaclass_name(self):
|
||||||
|
self.assertEqual(self.metacls.__name__, "PyCStructType")
|
||||||
|
|
||||||
|
def check_sizeof(self, cls, *, struct_size, union_size):
|
||||||
|
self.assertEqual(sizeof(cls), struct_size)
|
||||||
|
|
||||||
|
def test_simple_structs(self):
|
||||||
|
for code, tp in self.formats.items():
|
||||||
|
class X(Structure):
|
||||||
|
_fields_ = [("x", c_char),
|
||||||
|
("y", tp)]
|
||||||
|
self.assertEqual((sizeof(X), code),
|
||||||
|
(calcsize("c%c0%c" % (code, code)), code))
|
||||||
|
|
||||||
|
|
||||||
|
class UnionTestCase(unittest.TestCase, StructUnionTestBase):
|
||||||
|
cls = Union
|
||||||
|
metacls = UnionType
|
||||||
|
|
||||||
|
def test_metaclass_name(self):
|
||||||
|
self.assertEqual(self.metacls.__name__, "UnionType")
|
||||||
|
|
||||||
|
def check_sizeof(self, cls, *, struct_size, union_size):
|
||||||
|
self.assertEqual(sizeof(cls), union_size)
|
||||||
|
|
||||||
|
def test_simple_unions(self):
|
||||||
|
for code, tp in self.formats.items():
|
||||||
|
class X(Union):
|
||||||
|
_fields_ = [("x", c_char),
|
||||||
|
("y", tp)]
|
||||||
|
self.assertEqual((sizeof(X), code),
|
||||||
|
(calcsize("%c" % (code)), code))
|
||||||
|
|
||||||
|
|
||||||
|
class PointerMemberTestBase:
|
||||||
|
def test(self):
|
||||||
|
# a Structure/Union with a POINTER field
|
||||||
|
class S(self.cls):
|
||||||
|
_fields_ = [("array", POINTER(c_int))]
|
||||||
|
|
||||||
|
s = S()
|
||||||
|
# We can assign arrays of the correct type
|
||||||
|
s.array = (c_int * 3)(1, 2, 3)
|
||||||
|
items = [s.array[i] for i in range(3)]
|
||||||
|
self.assertEqual(items, [1, 2, 3])
|
||||||
|
|
||||||
|
s.array[0] = 42
|
||||||
|
|
||||||
|
items = [s.array[i] for i in range(3)]
|
||||||
|
self.assertEqual(items, [42, 2, 3])
|
||||||
|
|
||||||
|
s.array[0] = 1
|
||||||
|
|
||||||
|
items = [s.array[i] for i in range(3)]
|
||||||
|
self.assertEqual(items, [1, 2, 3])
|
||||||
|
|
||||||
|
class PointerMemberTestCase_Struct(unittest.TestCase, PointerMemberTestBase):
|
||||||
|
cls = Structure
|
||||||
|
|
||||||
|
def test_none_to_pointer_fields(self):
|
||||||
|
class S(self.cls):
|
||||||
|
_fields_ = [("x", c_int),
|
||||||
|
("p", POINTER(c_int))]
|
||||||
|
|
||||||
|
s = S()
|
||||||
|
s.x = 12345678
|
||||||
|
s.p = None
|
||||||
|
self.assertEqual(s.x, 12345678)
|
||||||
|
|
||||||
|
class PointerMemberTestCase_Union(unittest.TestCase, PointerMemberTestBase):
|
||||||
|
cls = Union
|
||||||
|
|
||||||
|
def test_none_to_pointer_fields(self):
|
||||||
|
class S(self.cls):
|
||||||
|
_fields_ = [("x", c_int),
|
||||||
|
("p", POINTER(c_int))]
|
||||||
|
|
||||||
|
s = S()
|
||||||
|
s.x = 12345678
|
||||||
|
s.p = None
|
||||||
|
self.assertFalse(s.p) # NULL pointers are falsy
|
||||||
|
|
||||||
|
|
||||||
|
class TestRecursiveBase:
|
||||||
|
def test_contains_itself(self):
|
||||||
|
class Recursive(self.cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
Recursive._fields_ = [("next", Recursive)]
|
||||||
|
except AttributeError as details:
|
||||||
|
self.assertIn("Structure or union cannot contain itself",
|
||||||
|
str(details))
|
||||||
|
else:
|
||||||
|
self.fail("Structure or union cannot contain itself")
|
||||||
|
|
||||||
|
|
||||||
|
def test_vice_versa(self):
|
||||||
|
class First(self.cls):
|
||||||
|
pass
|
||||||
|
class Second(self.cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
First._fields_ = [("second", Second)]
|
||||||
|
|
||||||
|
try:
|
||||||
|
Second._fields_ = [("first", First)]
|
||||||
|
except AttributeError as details:
|
||||||
|
self.assertIn("_fields_ is final", str(details))
|
||||||
|
else:
|
||||||
|
self.fail("AttributeError not raised")
|
||||||
|
|
||||||
|
class TestRecursiveStructure(unittest.TestCase, TestRecursiveBase):
|
||||||
|
cls = Structure
|
||||||
|
|
||||||
|
class TestRecursiveUnion(unittest.TestCase, TestRecursiveBase):
|
||||||
|
cls = Union
|
|
@ -1,209 +1,24 @@
|
||||||
|
"""Tests for ctypes.Structure
|
||||||
|
|
||||||
|
Features common with Union should go in test_structunion.py instead.
|
||||||
|
"""
|
||||||
|
|
||||||
from platform import architecture as _architecture
|
from platform import architecture as _architecture
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref, alignment,
|
from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref,
|
||||||
c_void_p, c_char, c_wchar, c_byte, c_ubyte,
|
c_void_p, c_char, c_wchar, c_byte, c_ubyte,
|
||||||
c_uint8, c_uint16, c_uint32,
|
c_uint8, c_uint16, c_uint32, c_int, c_uint,
|
||||||
c_short, c_ushort, c_int, c_uint,
|
c_long, c_ulong, c_longlong, c_float, c_double)
|
||||||
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
|
|
||||||
from ctypes.util import find_library
|
from ctypes.util import find_library
|
||||||
from struct import calcsize
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
_ctypes_test = import_helper.import_module("_ctypes_test")
|
_ctypes_test = import_helper.import_module("_ctypes_test")
|
||||||
from ._support import (_CData, PyCStructType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
|
||||||
Py_TPFLAGS_IMMUTABLETYPE)
|
|
||||||
|
|
||||||
|
|
||||||
class SubclassesTest(unittest.TestCase):
|
|
||||||
def test_subclass(self):
|
|
||||||
class X(Structure):
|
|
||||||
_fields_ = [("a", c_int)]
|
|
||||||
|
|
||||||
class Y(X):
|
|
||||||
_fields_ = [("b", c_int)]
|
|
||||||
|
|
||||||
class Z(X):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
|
||||||
self.assertEqual(sizeof(Y), sizeof(c_int)*2)
|
|
||||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
|
||||||
self.assertEqual(X._fields_, [("a", c_int)])
|
|
||||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
|
||||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
|
||||||
|
|
||||||
def test_subclass_delayed(self):
|
|
||||||
class X(Structure):
|
|
||||||
pass
|
|
||||||
self.assertEqual(sizeof(X), 0)
|
|
||||||
X._fields_ = [("a", c_int)]
|
|
||||||
|
|
||||||
class Y(X):
|
|
||||||
pass
|
|
||||||
self.assertEqual(sizeof(Y), sizeof(X))
|
|
||||||
Y._fields_ = [("b", c_int)]
|
|
||||||
|
|
||||||
class Z(X):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
|
||||||
self.assertEqual(sizeof(Y), sizeof(c_int)*2)
|
|
||||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
|
||||||
self.assertEqual(X._fields_, [("a", c_int)])
|
|
||||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
|
||||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
|
||||||
|
|
||||||
|
|
||||||
class StructureTestCase(unittest.TestCase):
|
class StructureTestCase(unittest.TestCase):
|
||||||
formats = {"c": c_char,
|
|
||||||
"b": c_byte,
|
|
||||||
"B": c_ubyte,
|
|
||||||
"h": c_short,
|
|
||||||
"H": c_ushort,
|
|
||||||
"i": c_int,
|
|
||||||
"I": c_uint,
|
|
||||||
"l": c_long,
|
|
||||||
"L": c_ulong,
|
|
||||||
"q": c_longlong,
|
|
||||||
"Q": c_ulonglong,
|
|
||||||
"f": c_float,
|
|
||||||
"d": c_double,
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_inheritance_hierarchy(self):
|
|
||||||
self.assertEqual(Structure.mro(), [Structure, _CData, object])
|
|
||||||
|
|
||||||
self.assertEqual(PyCStructType.__name__, "PyCStructType")
|
|
||||||
self.assertEqual(type(PyCStructType), type)
|
|
||||||
|
|
||||||
|
|
||||||
def test_type_flags(self):
|
|
||||||
for cls in Structure, PyCStructType:
|
|
||||||
with self.subTest(cls=cls):
|
|
||||||
self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
|
||||||
self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
|
||||||
|
|
||||||
def test_metaclass_details(self):
|
|
||||||
# Abstract classes (whose metaclass __init__ was not called) can't be
|
|
||||||
# instantiated directly
|
|
||||||
NewStructure = PyCStructType.__new__(PyCStructType, 'NewStructure',
|
|
||||||
(Structure,), {})
|
|
||||||
for cls in Structure, NewStructure:
|
|
||||||
with self.subTest(cls=cls):
|
|
||||||
with self.assertRaisesRegex(TypeError, "abstract class"):
|
|
||||||
obj = cls()
|
|
||||||
|
|
||||||
# Cannot call the metaclass __init__ more than once
|
|
||||||
class T(Structure):
|
|
||||||
_fields_ = [("x", c_char),
|
|
||||||
("y", c_char)]
|
|
||||||
with self.assertRaisesRegex(SystemError, "already initialized"):
|
|
||||||
PyCStructType.__init__(T, 'ptr', (), {})
|
|
||||||
|
|
||||||
def test_simple_structs(self):
|
|
||||||
for code, tp in self.formats.items():
|
|
||||||
class X(Structure):
|
|
||||||
_fields_ = [("x", c_char),
|
|
||||||
("y", tp)]
|
|
||||||
self.assertEqual((sizeof(X), code),
|
|
||||||
(calcsize("c%c0%c" % (code, code)), code))
|
|
||||||
|
|
||||||
def test_unions(self):
|
|
||||||
for code, tp in self.formats.items():
|
|
||||||
class X(Union):
|
|
||||||
_fields_ = [("x", c_char),
|
|
||||||
("y", tp)]
|
|
||||||
self.assertEqual((sizeof(X), code),
|
|
||||||
(calcsize("%c" % (code)), code))
|
|
||||||
|
|
||||||
def test_struct_alignment(self):
|
|
||||||
class X(Structure):
|
|
||||||
_fields_ = [("x", c_char * 3)]
|
|
||||||
self.assertEqual(alignment(X), calcsize("s"))
|
|
||||||
self.assertEqual(sizeof(X), calcsize("3s"))
|
|
||||||
|
|
||||||
class Y(Structure):
|
|
||||||
_fields_ = [("x", c_char * 3),
|
|
||||||
("y", c_int)]
|
|
||||||
self.assertEqual(alignment(Y), alignment(c_int))
|
|
||||||
self.assertEqual(sizeof(Y), calcsize("3si"))
|
|
||||||
|
|
||||||
class SI(Structure):
|
|
||||||
_fields_ = [("a", X),
|
|
||||||
("b", Y)]
|
|
||||||
self.assertEqual(alignment(SI), max(alignment(Y), alignment(X)))
|
|
||||||
self.assertEqual(sizeof(SI), calcsize("3s0i 3si 0i"))
|
|
||||||
|
|
||||||
class IS(Structure):
|
|
||||||
_fields_ = [("b", Y),
|
|
||||||
("a", X)]
|
|
||||||
|
|
||||||
self.assertEqual(alignment(SI), max(alignment(X), alignment(Y)))
|
|
||||||
self.assertEqual(sizeof(IS), calcsize("3si 3s 0i"))
|
|
||||||
|
|
||||||
class XX(Structure):
|
|
||||||
_fields_ = [("a", X),
|
|
||||||
("b", X)]
|
|
||||||
self.assertEqual(alignment(XX), alignment(X))
|
|
||||||
self.assertEqual(sizeof(XX), calcsize("3s 3s 0s"))
|
|
||||||
|
|
||||||
def test_empty(self):
|
|
||||||
# I had problems with these
|
|
||||||
#
|
|
||||||
# Although these are pathological cases: Empty Structures!
|
|
||||||
class X(Structure):
|
|
||||||
_fields_ = []
|
|
||||||
|
|
||||||
class Y(Union):
|
|
||||||
_fields_ = []
|
|
||||||
|
|
||||||
# Is this really the correct alignment, or should it be 0?
|
|
||||||
self.assertTrue(alignment(X) == alignment(Y) == 1)
|
|
||||||
self.assertTrue(sizeof(X) == sizeof(Y) == 0)
|
|
||||||
|
|
||||||
class XX(Structure):
|
|
||||||
_fields_ = [("a", X),
|
|
||||||
("b", X)]
|
|
||||||
|
|
||||||
self.assertEqual(alignment(XX), 1)
|
|
||||||
self.assertEqual(sizeof(XX), 0)
|
|
||||||
|
|
||||||
def test_fields(self):
|
|
||||||
# test the offset and size attributes of Structure/Union fields.
|
|
||||||
class X(Structure):
|
|
||||||
_fields_ = [("x", c_int),
|
|
||||||
("y", c_char)]
|
|
||||||
|
|
||||||
self.assertEqual(X.x.offset, 0)
|
|
||||||
self.assertEqual(X.x.size, sizeof(c_int))
|
|
||||||
|
|
||||||
self.assertEqual(X.y.offset, sizeof(c_int))
|
|
||||||
self.assertEqual(X.y.size, sizeof(c_char))
|
|
||||||
|
|
||||||
# readonly
|
|
||||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
|
||||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
|
||||||
|
|
||||||
class X(Union):
|
|
||||||
_fields_ = [("x", c_int),
|
|
||||||
("y", c_char)]
|
|
||||||
|
|
||||||
self.assertEqual(X.x.offset, 0)
|
|
||||||
self.assertEqual(X.x.size, sizeof(c_int))
|
|
||||||
|
|
||||||
self.assertEqual(X.y.offset, 0)
|
|
||||||
self.assertEqual(X.y.size, sizeof(c_char))
|
|
||||||
|
|
||||||
# readonly
|
|
||||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
|
||||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
|
||||||
|
|
||||||
# XXX Should we check nested data types also?
|
|
||||||
# offset is always relative to the class...
|
|
||||||
|
|
||||||
def test_packed(self):
|
def test_packed(self):
|
||||||
class X(Structure):
|
class X(Structure):
|
||||||
_fields_ = [("a", c_byte),
|
_fields_ = [("a", c_byte),
|
||||||
|
@ -290,36 +105,6 @@ class StructureTestCase(unittest.TestCase):
|
||||||
pt = POINT(y=2, x=1)
|
pt = POINT(y=2, x=1)
|
||||||
self.assertEqual((pt.x, pt.y), (1, 2))
|
self.assertEqual((pt.x, pt.y), (1, 2))
|
||||||
|
|
||||||
def test_invalid_field_types(self):
|
|
||||||
class POINT(Structure):
|
|
||||||
pass
|
|
||||||
self.assertRaises(TypeError, setattr, POINT, "_fields_", [("x", 1), ("y", 2)])
|
|
||||||
|
|
||||||
def test_invalid_name(self):
|
|
||||||
# field name must be string
|
|
||||||
def declare_with_name(name):
|
|
||||||
class S(Structure):
|
|
||||||
_fields_ = [(name, c_int)]
|
|
||||||
|
|
||||||
self.assertRaises(TypeError, declare_with_name, b"x")
|
|
||||||
|
|
||||||
def test_intarray_fields(self):
|
|
||||||
class SomeInts(Structure):
|
|
||||||
_fields_ = [("a", c_int * 4)]
|
|
||||||
|
|
||||||
# can use tuple to initialize array (but not list!)
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0])
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0])
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[::-1], [0, 0, 2, 1])
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[::2], [1, 0])
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[1:5:6], [2])
|
|
||||||
self.assertEqual(SomeInts((1, 2)).a[6:4:-1], [])
|
|
||||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4])
|
|
||||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4])
|
|
||||||
# too long
|
|
||||||
# XXX Should raise ValueError?, not RuntimeError
|
|
||||||
self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5))
|
|
||||||
|
|
||||||
def test_nested_initializers(self):
|
def test_nested_initializers(self):
|
||||||
# test initializing nested structures
|
# test initializing nested structures
|
||||||
class Phone(Structure):
|
class Phone(Structure):
|
||||||
|
@ -374,37 +159,12 @@ class StructureTestCase(unittest.TestCase):
|
||||||
self.assertEqual(msg,
|
self.assertEqual(msg,
|
||||||
"(Phone) TypeError: too many initializers")
|
"(Phone) TypeError: too many initializers")
|
||||||
|
|
||||||
def test_huge_field_name(self):
|
|
||||||
# issue12881: segfault with large structure field names
|
|
||||||
def create_class(length):
|
|
||||||
class S(Structure):
|
|
||||||
_fields_ = [('x' * length, c_int)]
|
|
||||||
|
|
||||||
for length in [10 ** i for i in range(0, 8)]:
|
|
||||||
try:
|
|
||||||
create_class(length)
|
|
||||||
except MemoryError:
|
|
||||||
# MemoryErrors are OK, we just don't want to segfault
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_except(self, func, *args):
|
def get_except(self, func, *args):
|
||||||
try:
|
try:
|
||||||
func(*args)
|
func(*args)
|
||||||
except Exception as detail:
|
except Exception as detail:
|
||||||
return detail.__class__, str(detail)
|
return detail.__class__, str(detail)
|
||||||
|
|
||||||
def test_abstract_class(self):
|
|
||||||
class X(Structure):
|
|
||||||
_abstract_ = "something"
|
|
||||||
# try 'X()'
|
|
||||||
cls, msg = self.get_except(eval, "X()", locals())
|
|
||||||
self.assertEqual((cls, msg), (TypeError, "abstract class"))
|
|
||||||
|
|
||||||
def test_methods(self):
|
|
||||||
self.assertIn("in_dll", dir(type(Structure)))
|
|
||||||
self.assertIn("from_address", dir(type(Structure)))
|
|
||||||
self.assertIn("in_dll", dir(type(Structure)))
|
|
||||||
|
|
||||||
def test_positional_args(self):
|
def test_positional_args(self):
|
||||||
# see also http://bugs.python.org/issue5042
|
# see also http://bugs.python.org/issue5042
|
||||||
class W(Structure):
|
class W(Structure):
|
||||||
|
@ -507,6 +267,8 @@ class StructureTestCase(unittest.TestCase):
|
||||||
self.assertEqual(s.second, got.second)
|
self.assertEqual(s.second, got.second)
|
||||||
|
|
||||||
def _test_issue18060(self, Vector):
|
def _test_issue18060(self, Vector):
|
||||||
|
# Regression tests for gh-62260
|
||||||
|
|
||||||
# The call to atan2() should succeed if the
|
# The call to atan2() should succeed if the
|
||||||
# class fields were correctly cloned in the
|
# class fields were correctly cloned in the
|
||||||
# subclasses. Otherwise, it will segfault.
|
# subclasses. Otherwise, it will segfault.
|
||||||
|
@ -698,6 +460,7 @@ class StructureTestCase(unittest.TestCase):
|
||||||
self.assertEqual(result.data[i], float(i+1))
|
self.assertEqual(result.data[i], float(i+1))
|
||||||
|
|
||||||
def test_38368(self):
|
def test_38368(self):
|
||||||
|
# Regression test for gh-82549
|
||||||
class U(Union):
|
class U(Union):
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('f1', c_uint8 * 16),
|
('f1', c_uint8 * 16),
|
||||||
|
@ -719,9 +482,9 @@ class StructureTestCase(unittest.TestCase):
|
||||||
self.assertEqual(f2, [0x4567, 0x0123, 0xcdef, 0x89ab,
|
self.assertEqual(f2, [0x4567, 0x0123, 0xcdef, 0x89ab,
|
||||||
0x3210, 0x7654, 0xba98, 0xfedc])
|
0x3210, 0x7654, 0xba98, 0xfedc])
|
||||||
|
|
||||||
@unittest.skipIf(True, 'Test disabled for now - see bpo-16575/bpo-16576')
|
@unittest.skipIf(True, 'Test disabled for now - see gh-60779/gh-60780')
|
||||||
def test_union_by_value(self):
|
def test_union_by_value(self):
|
||||||
# See bpo-16575
|
# See gh-60779
|
||||||
|
|
||||||
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
||||||
|
|
||||||
|
@ -800,9 +563,9 @@ class StructureTestCase(unittest.TestCase):
|
||||||
self.assertEqual(test5.nested.an_int, 0)
|
self.assertEqual(test5.nested.an_int, 0)
|
||||||
self.assertEqual(test5.another_int, 0)
|
self.assertEqual(test5.another_int, 0)
|
||||||
|
|
||||||
@unittest.skipIf(True, 'Test disabled for now - see bpo-16575/bpo-16576')
|
@unittest.skipIf(True, 'Test disabled for now - see gh-60779/gh-60780')
|
||||||
def test_bitfield_by_value(self):
|
def test_bitfield_by_value(self):
|
||||||
# See bpo-16576
|
# See gh-60780
|
||||||
|
|
||||||
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
||||||
|
|
||||||
|
@ -882,75 +645,5 @@ class StructureTestCase(unittest.TestCase):
|
||||||
'a union by value, which is unsupported.')
|
'a union by value, which is unsupported.')
|
||||||
|
|
||||||
|
|
||||||
class PointerMemberTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test(self):
|
|
||||||
# a Structure with a POINTER field
|
|
||||||
class S(Structure):
|
|
||||||
_fields_ = [("array", POINTER(c_int))]
|
|
||||||
|
|
||||||
s = S()
|
|
||||||
# We can assign arrays of the correct type
|
|
||||||
s.array = (c_int * 3)(1, 2, 3)
|
|
||||||
items = [s.array[i] for i in range(3)]
|
|
||||||
self.assertEqual(items, [1, 2, 3])
|
|
||||||
|
|
||||||
# The following are bugs, but are included here because the unittests
|
|
||||||
# also describe the current behaviour.
|
|
||||||
#
|
|
||||||
# This fails with SystemError: bad arg to internal function
|
|
||||||
# or with IndexError (with a patch I have)
|
|
||||||
|
|
||||||
s.array[0] = 42
|
|
||||||
|
|
||||||
items = [s.array[i] for i in range(3)]
|
|
||||||
self.assertEqual(items, [42, 2, 3])
|
|
||||||
|
|
||||||
s.array[0] = 1
|
|
||||||
|
|
||||||
items = [s.array[i] for i in range(3)]
|
|
||||||
self.assertEqual(items, [1, 2, 3])
|
|
||||||
|
|
||||||
def test_none_to_pointer_fields(self):
|
|
||||||
class S(Structure):
|
|
||||||
_fields_ = [("x", c_int),
|
|
||||||
("p", POINTER(c_int))]
|
|
||||||
|
|
||||||
s = S()
|
|
||||||
s.x = 12345678
|
|
||||||
s.p = None
|
|
||||||
self.assertEqual(s.x, 12345678)
|
|
||||||
|
|
||||||
|
|
||||||
class TestRecursiveStructure(unittest.TestCase):
|
|
||||||
def test_contains_itself(self):
|
|
||||||
class Recursive(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
Recursive._fields_ = [("next", Recursive)]
|
|
||||||
except AttributeError as details:
|
|
||||||
self.assertIn("Structure or union cannot contain itself",
|
|
||||||
str(details))
|
|
||||||
else:
|
|
||||||
self.fail("Structure or union cannot contain itself")
|
|
||||||
|
|
||||||
|
|
||||||
def test_vice_versa(self):
|
|
||||||
class First(Structure):
|
|
||||||
pass
|
|
||||||
class Second(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
First._fields_ = [("second", Second)]
|
|
||||||
|
|
||||||
try:
|
|
||||||
Second._fields_ = [("first", First)]
|
|
||||||
except AttributeError as details:
|
|
||||||
self.assertIn("_fields_ is final", str(details))
|
|
||||||
else:
|
|
||||||
self.fail("AttributeError not raised")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import unittest
|
|
||||||
from ctypes import Union, c_char
|
|
||||||
from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
|
||||||
Py_TPFLAGS_IMMUTABLETYPE)
|
|
||||||
|
|
||||||
|
|
||||||
class ArrayTestCase(unittest.TestCase):
|
|
||||||
def test_inheritance_hierarchy(self):
|
|
||||||
self.assertEqual(Union.mro(), [Union, _CData, object])
|
|
||||||
|
|
||||||
self.assertEqual(UnionType.__name__, "UnionType")
|
|
||||||
self.assertEqual(type(UnionType), type)
|
|
||||||
|
|
||||||
def test_type_flags(self):
|
|
||||||
for cls in Union, UnionType:
|
|
||||||
with self.subTest(cls=Union):
|
|
||||||
self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
|
||||||
self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
|
||||||
|
|
||||||
def test_metaclass_details(self):
|
|
||||||
# Abstract classes (whose metaclass __init__ was not called) can't be
|
|
||||||
# instantiated directly
|
|
||||||
NewUnion = UnionType.__new__(UnionType, 'NewUnion',
|
|
||||||
(Union,), {})
|
|
||||||
for cls in Union, NewUnion:
|
|
||||||
with self.subTest(cls=cls):
|
|
||||||
with self.assertRaisesRegex(TypeError, "abstract class"):
|
|
||||||
obj = cls()
|
|
||||||
|
|
||||||
# Cannot call the metaclass __init__ more than once
|
|
||||||
class T(Union):
|
|
||||||
_fields_ = [("x", c_char),
|
|
||||||
("y", c_char)]
|
|
||||||
with self.assertRaisesRegex(SystemError, "already initialized"):
|
|
||||||
UnionType.__init__(T, 'ptr', (), {})
|
|
Loading…
Add table
Add a link
Reference in a new issue