Abstract base class for unions in native byte order.
+ Unions share common attributes and behavior with structures;
+ see :class:`Structure` documentation for details.
.. class:: BigEndianUnion(*args, **kw)
...
]
- The :attr:`_fields_` class variable must, however, be defined before the
- type is first used (an instance is created, :func:`sizeof` is called on it,
- and so on). Later assignments to the :attr:`_fields_` class variable will
- raise an AttributeError.
+ The :attr:`!_fields_` class variable can only be set once.
+ Later assignments will raise an :exc:`AttributeError`.
- It is possible to define sub-subclasses of structure types, they inherit
- the fields of the base class plus the :attr:`_fields_` defined in the
- sub-subclass, if any.
+ Additionally, the :attr:`!_fields_` class variable must be defined before
+ the structure or union type is first used: an instance or subclass is
+ created, :func:`sizeof` is called on it, and so on.
+ Later assignments to :attr:`!_fields_` will raise an :exc:`AttributeError`.
+ If :attr:`!_fields_` has not been set before such use,
+ the structure or union will have no own fields, as if :attr:`!_fields_`
+ was empty.
+
+ Sub-subclasses of structure types inherit the fields of the base class
+ plus the :attr:`_fields_` defined in the sub-subclass, if any.
.. attribute:: _pack_
Py_TPFLAGS_IMMUTABLETYPE)
-class StructFieldsTestCase(unittest.TestCase):
+NOTHING = object()
+
+class FieldsTestBase:
# Structure/Union classes must get 'finalized' sooner or
# later, when one of these things happen:
#
# 4. The type is subclassed
#
# When they are finalized, assigning _fields_ is no longer allowed.
+
+ def assert_final_fields(self, cls, expected=NOTHING):
+ self.assertRaises(AttributeError, setattr, cls, "_fields_", [])
+ self.assertEqual(getattr(cls, "_fields_", NOTHING), expected)
+
def test_1_A(self):
- class X(Structure):
+ class X(self.cls):
pass
self.assertEqual(sizeof(X), 0) # not finalized
X._fields_ = [] # finalized
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X, expected=[])
def test_1_B(self):
- class X(Structure):
+ class X(self.cls):
_fields_ = [] # finalized
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X, expected=[])
def test_2(self):
- class X(Structure):
+ class X(self.cls):
pass
X()
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X)
def test_3(self):
- class X(Structure):
+ class X(self.cls):
pass
- class Y(Structure):
+ class Y(self.cls):
_fields_ = [("x", X)] # finalizes X
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X)
def test_4(self):
- class X(Structure):
+ class X(self.cls):
pass
class Y(X):
pass
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X)
Y._fields_ = []
- self.assertRaises(AttributeError, setattr, X, "_fields_", [])
+ self.assert_final_fields(X)
def test_5(self):
- class X(Structure):
+ class X(self.cls):
_fields_ = (("char", c_char * 5),)
x = X(b'#' * 5)
def test_6(self):
self.assertRaises(TypeError, CField)
- def test_cfield_type_flags(self):
- self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
-
- def test_cfield_inheritance_hierarchy(self):
- self.assertEqual(CField.mro(), [CField, object])
-
def test_gh99275(self):
- class BrokenStructure(Structure):
+ class BrokenStructure(self.cls):
def __init_subclass__(cls, **kwargs):
cls._fields_ = [] # This line will fail, `stginfo` is not ready
# __set__ and __get__ should raise a TypeError in case their self
# argument is not a ctype instance.
def test___set__(self):
- class MyCStruct(Structure):
+ class MyCStruct(self.cls):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCStruct.field.__set__, 'wrong type self', 42)
- class MyCUnion(Union):
- _fields_ = (("field", c_int),)
- self.assertRaises(TypeError,
- MyCUnion.field.__set__, 'wrong type self', 42)
-
def test___get__(self):
- class MyCStruct(Structure):
+ class MyCStruct(self.cls):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCStruct.field.__get__, 'wrong type self', 42)
- class MyCUnion(Union):
- _fields_ = (("field", c_int),)
- self.assertRaises(TypeError,
- MyCUnion.field.__get__, 'wrong type self', 42)
+class StructFieldsTestCase(unittest.TestCase, FieldsTestBase):
+ cls = Structure
+
+ def test_cfield_type_flags(self):
+ self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+
+ def test_cfield_inheritance_hierarchy(self):
+ self.assertEqual(CField.mro(), [CField, object])
+
+class UnionFieldsTestCase(unittest.TestCase, FieldsTestBase):
+ cls = Union
if __name__ == "__main__":
return PyCArrayType_from_ctype(st, self, length);
}
-
static int
-PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value)
+_structunion_setattro(PyObject *self, PyObject *key, PyObject *value, int is_struct)
{
/* XXX Should we disallow deleting _fields_? */
- if (-1 == PyType_Type.tp_setattro(self, key, value))
- return -1;
+ if (PyUnicode_Check(key)
+ && _PyUnicode_EqualToASCIIString(key, "_fields_"))
+ {
+ if (PyCStructUnionType_update_stginfo(self, value, is_struct) < 0) {
+ return -1;
+ }
+ }
- if (value && PyUnicode_Check(key) &&
- _PyUnicode_EqualToASCIIString(key, "_fields_"))
- return PyCStructUnionType_update_stginfo(self, value, 1);
- return 0;
+ return PyType_Type.tp_setattro(self, key, value);
}
+static int
+PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value)
+{
+ return _structunion_setattro(self, key, value, 1);
+}
static int
UnionType_setattro(PyObject *self, PyObject *key, PyObject *value)
{
- /* XXX Should we disallow deleting _fields_? */
- if (-1 == PyType_Type.tp_setattro(self, key, value))
- return -1;
-
- if (PyUnicode_Check(key) &&
- _PyUnicode_EqualToASCIIString(key, "_fields_"))
- return PyCStructUnionType_update_stginfo(self, value, 0);
- return 0;
+ return _structunion_setattro(self, key, value, 0);
}
static PyType_Slot pycstruct_type_slots[] = {