Skip to content

Commit

Permalink
[3.12] gh-112358: Fix Python 3.12 regression with subclassing struct.…
Browse files Browse the repository at this point in the history
…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 <dickinsm@gmail.com>

* Remove unrelated test
  • Loading branch information
mdickinson authored Nov 27, 2023
1 parent d7a7883 commit 42df736
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 48 deletions.
24 changes: 10 additions & 14 deletions Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,20 +700,6 @@ def test__struct_types_immutable(self):
with self.assertRaises(TypeError):
cls.x = 1

@support.cpython_only
def test__struct_Struct__new__initialized(self):
# See https://github.com/python/cpython/issues/78724

s = struct.Struct.__new__(struct.Struct, "b")
s.unpack_from(b"abcd")

@support.cpython_only
def test__struct_Struct_subclassing(self):
class Bob(struct.Struct):
pass

s = Bob("b")
s.unpack_from(b"abcd")

def test_issue35714(self):
# Embedded null characters should not be allowed in format strings.
Expand Down Expand Up @@ -774,6 +760,16 @@ def test_error_propagation(fmt_str):
test_error_propagation('N')
test_error_propagation('n')

def test_struct_subclass_instantiation(self):
# Regression test for https://github.com/python/cpython/issues/112358
class MyStruct(struct.Struct):
def __init__(self):
super().__init__('>h')

my_struct = MyStruct()
self.assertEqual(my_struct.pack(12345), b'\x30\x39')


class UnpackIteratorTest(unittest.TestCase):
"""
Tests for iterative unpacking (struct.Struct.iter_unpack).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Revert change to :class:`struct.Struct` initialization that broke some cases
of subclassing.
59 changes: 33 additions & 26 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -1550,9 +1550,28 @@ prepare_s(PyStructObject *self)
return -1;
}

static PyObject *
s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *self;

assert(type != NULL);
allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc);
assert(alloc_func != NULL);

self = alloc_func(type, 0);
if (self != NULL) {
PyStructObject *s = (PyStructObject*)self;
s->s_format = Py_NewRef(Py_None);
s->s_codes = NULL;
s->s_size = -1;
s->s_len = -1;
}
return self;
}

/*[clinic input]
@classmethod
Struct.__new__
Struct.__init__
format: object
Expand All @@ -1564,49 +1583,36 @@ the format string.
See help(struct) for more on format strings.
[clinic start generated code]*/

static PyObject *
Struct_impl(PyTypeObject *type, PyObject *format)
/*[clinic end generated code: output=49468b044e334308 input=8b91868eb1df0e28]*/
static int
Struct___init___impl(PyStructObject *self, PyObject *format)
/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/
{
allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc);
assert(alloc != NULL);
PyStructObject *self = (PyStructObject *)alloc(type, 0);

if (self == NULL) {
return NULL;
}
int ret = 0;

if (PyUnicode_Check(format)) {
format = PyUnicode_AsASCIIString(format);
if (format == NULL) {
Py_DECREF(self);
return NULL;
}
if (format == NULL)
return -1;
}
else {
Py_INCREF(format);
}

if (!PyBytes_Check(format)) {
Py_DECREF(format);
Py_DECREF(self);
PyErr_Format(PyExc_TypeError,
"Struct() argument 1 must be a str or bytes object, "
"not %.200s",
_PyType_Name(Py_TYPE(format)));
return NULL;
return -1;
}

self->s_format = format;
Py_SETREF(self->s_format, format);

if (prepare_s(self) < 0) {
Py_DECREF(self);
return NULL;
}
return (PyObject *)self;
ret = prepare_s(self);
return ret;
}


static int
s_clear(PyStructObject *s)
{
Expand Down Expand Up @@ -2202,8 +2208,9 @@ static PyType_Slot PyStructType_slots[] = {
{Py_tp_methods, s_methods},
{Py_tp_members, s_members},
{Py_tp_getset, s_getsetlist},
{Py_tp_new, Struct},
{Py_tp_init, Struct___init__},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, s_new},
{Py_tp_free, PyObject_GC_Del},
{0, 0},
};
Expand Down
16 changes: 8 additions & 8 deletions Modules/clinic/_struct.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 42df736

Please sign in to comment.