Skip to content

Commit 42df736

Browse files
authored
[3.12] gh-112358: Fix Python 3.12 regression with subclassing struct.Struct (GH-112424) (#112426)
* [3.12] gh-112358: Fix Python 3.12 regression with subclassing struct.Struct. (GH-112424) Revert commit c8c0afc (PR GH-94532), which moved `struct.Struct` initialisation from `Struct.__init__` to `Struct.__new__`. This caused issues with code in the wild that subclasses `struct.Struct`.. (cherry picked from commit 9fe6034) Co-authored-by: Mark Dickinson <[email protected]> * Remove unrelated test
1 parent d7a7883 commit 42df736

File tree

4 files changed

+53
-48
lines changed

4 files changed

+53
-48
lines changed

Lib/test/test_struct.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -700,20 +700,6 @@ def test__struct_types_immutable(self):
700700
with self.assertRaises(TypeError):
701701
cls.x = 1
702702

703-
@support.cpython_only
704-
def test__struct_Struct__new__initialized(self):
705-
# See https://github.com/python/cpython/issues/78724
706-
707-
s = struct.Struct.__new__(struct.Struct, "b")
708-
s.unpack_from(b"abcd")
709-
710-
@support.cpython_only
711-
def test__struct_Struct_subclassing(self):
712-
class Bob(struct.Struct):
713-
pass
714-
715-
s = Bob("b")
716-
s.unpack_from(b"abcd")
717703

718704
def test_issue35714(self):
719705
# Embedded null characters should not be allowed in format strings.
@@ -774,6 +760,16 @@ def test_error_propagation(fmt_str):
774760
test_error_propagation('N')
775761
test_error_propagation('n')
776762

763+
def test_struct_subclass_instantiation(self):
764+
# Regression test for https://github.com/python/cpython/issues/112358
765+
class MyStruct(struct.Struct):
766+
def __init__(self):
767+
super().__init__('>h')
768+
769+
my_struct = MyStruct()
770+
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
771+
772+
777773
class UnpackIteratorTest(unittest.TestCase):
778774
"""
779775
Tests for iterative unpacking (struct.Struct.iter_unpack).
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Revert change to :class:`struct.Struct` initialization that broke some cases
2+
of subclassing.

Modules/_struct.c

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,9 +1550,28 @@ prepare_s(PyStructObject *self)
15501550
return -1;
15511551
}
15521552

1553+
static PyObject *
1554+
s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1555+
{
1556+
PyObject *self;
1557+
1558+
assert(type != NULL);
1559+
allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc);
1560+
assert(alloc_func != NULL);
1561+
1562+
self = alloc_func(type, 0);
1563+
if (self != NULL) {
1564+
PyStructObject *s = (PyStructObject*)self;
1565+
s->s_format = Py_NewRef(Py_None);
1566+
s->s_codes = NULL;
1567+
s->s_size = -1;
1568+
s->s_len = -1;
1569+
}
1570+
return self;
1571+
}
1572+
15531573
/*[clinic input]
1554-
@classmethod
1555-
Struct.__new__
1574+
Struct.__init__
15561575
15571576
format: object
15581577
@@ -1564,49 +1583,36 @@ the format string.
15641583
See help(struct) for more on format strings.
15651584
[clinic start generated code]*/
15661585

1567-
static PyObject *
1568-
Struct_impl(PyTypeObject *type, PyObject *format)
1569-
/*[clinic end generated code: output=49468b044e334308 input=8b91868eb1df0e28]*/
1586+
static int
1587+
Struct___init___impl(PyStructObject *self, PyObject *format)
1588+
/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/
15701589
{
1571-
allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc);
1572-
assert(alloc != NULL);
1573-
PyStructObject *self = (PyStructObject *)alloc(type, 0);
1574-
1575-
if (self == NULL) {
1576-
return NULL;
1577-
}
1590+
int ret = 0;
15781591

15791592
if (PyUnicode_Check(format)) {
15801593
format = PyUnicode_AsASCIIString(format);
1581-
if (format == NULL) {
1582-
Py_DECREF(self);
1583-
return NULL;
1584-
}
1594+
if (format == NULL)
1595+
return -1;
15851596
}
15861597
else {
15871598
Py_INCREF(format);
15881599
}
15891600

15901601
if (!PyBytes_Check(format)) {
15911602
Py_DECREF(format);
1592-
Py_DECREF(self);
15931603
PyErr_Format(PyExc_TypeError,
15941604
"Struct() argument 1 must be a str or bytes object, "
15951605
"not %.200s",
15961606
_PyType_Name(Py_TYPE(format)));
1597-
return NULL;
1607+
return -1;
15981608
}
15991609

1600-
self->s_format = format;
1610+
Py_SETREF(self->s_format, format);
16011611

1602-
if (prepare_s(self) < 0) {
1603-
Py_DECREF(self);
1604-
return NULL;
1605-
}
1606-
return (PyObject *)self;
1612+
ret = prepare_s(self);
1613+
return ret;
16071614
}
16081615

1609-
16101616
static int
16111617
s_clear(PyStructObject *s)
16121618
{
@@ -2202,8 +2208,9 @@ static PyType_Slot PyStructType_slots[] = {
22022208
{Py_tp_methods, s_methods},
22032209
{Py_tp_members, s_members},
22042210
{Py_tp_getset, s_getsetlist},
2205-
{Py_tp_new, Struct},
2211+
{Py_tp_init, Struct___init__},
22062212
{Py_tp_alloc, PyType_GenericAlloc},
2213+
{Py_tp_new, s_new},
22072214
{Py_tp_free, PyObject_GC_Del},
22082215
{0, 0},
22092216
};

Modules/clinic/_struct.c.h

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)