gh-112358: Fix Python 3.12 regression with subclassing struct.Struct. (#112424)

Revert commit c8c0afc713 (PR #94532),
which moved `struct.Struct` initialisation from `Struct.__init__` to `Struct.__new__`.
This caused issues with code in the wild that subclasses `struct.Struct`.
This commit is contained in:
Mark Dickinson 2023-11-26 14:29:52 +00:00 committed by GitHub
parent 3faf8e586d
commit 9fe60340d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 48 deletions

View File

@ -700,20 +700,6 @@ class StructTest(unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
cls.x = 1 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): def test_issue35714(self):
# Embedded null characters should not be allowed in format strings. # Embedded null characters should not be allowed in format strings.
@ -774,6 +760,15 @@ class StructTest(unittest.TestCase):
test_error_propagation('N') test_error_propagation('N')
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')
def test_repr(self): def test_repr(self):
s = struct.Struct('=i2H') s = struct.Struct('=i2H')
self.assertEqual(repr(s), f'Struct({s.format!r})') self.assertEqual(repr(s), f'Struct({s.format!r})')

View File

@ -0,0 +1,2 @@
Revert change to :class:`struct.Struct` initialization that broke some cases
of subclassing.

View File

@ -1553,9 +1553,28 @@ prepare_s(PyStructObject *self)
return -1; 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] /*[clinic input]
@classmethod Struct.__init__
Struct.__new__
format: object format: object
@ -1567,24 +1586,16 @@ the format string.
See help(struct) for more on format strings. See help(struct) for more on format strings.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static int
Struct_impl(PyTypeObject *type, PyObject *format) Struct___init___impl(PyStructObject *self, PyObject *format)
/*[clinic end generated code: output=49468b044e334308 input=8b91868eb1df0e28]*/ /*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/
{ {
allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc); int ret = 0;
assert(alloc != NULL);
PyStructObject *self = (PyStructObject *)alloc(type, 0);
if (self == NULL) {
return NULL;
}
if (PyUnicode_Check(format)) { if (PyUnicode_Check(format)) {
format = PyUnicode_AsASCIIString(format); format = PyUnicode_AsASCIIString(format);
if (format == NULL) { if (format == NULL)
Py_DECREF(self); return -1;
return NULL;
}
} }
else { else {
Py_INCREF(format); Py_INCREF(format);
@ -1592,24 +1603,19 @@ Struct_impl(PyTypeObject *type, PyObject *format)
if (!PyBytes_Check(format)) { if (!PyBytes_Check(format)) {
Py_DECREF(format); Py_DECREF(format);
Py_DECREF(self);
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"Struct() argument 1 must be a str or bytes object, " "Struct() argument 1 must be a str or bytes object, "
"not %.200s", "not %.200s",
_PyType_Name(Py_TYPE(format))); _PyType_Name(Py_TYPE(format)));
return NULL; return -1;
} }
self->s_format = format; Py_SETREF(self->s_format, format);
if (prepare_s(self) < 0) { ret = prepare_s(self);
Py_DECREF(self); return ret;
return NULL;
}
return (PyObject *)self;
} }
static int static int
s_clear(PyStructObject *s) s_clear(PyStructObject *s)
{ {
@ -2219,8 +2225,9 @@ static PyType_Slot PyStructType_slots[] = {
{Py_tp_methods, s_methods}, {Py_tp_methods, s_methods},
{Py_tp_members, s_members}, {Py_tp_members, s_members},
{Py_tp_getset, s_getsetlist}, {Py_tp_getset, s_getsetlist},
{Py_tp_new, Struct}, {Py_tp_init, Struct___init__},
{Py_tp_alloc, PyType_GenericAlloc}, {Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, s_new},
{Py_tp_free, PyObject_GC_Del}, {Py_tp_free, PyObject_GC_Del},
{0, 0}, {0, 0},
}; };

View File

@ -9,7 +9,7 @@ preserve
#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_abstract.h" // _PyNumber_Index()
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(Struct__doc__, PyDoc_STRVAR(Struct___init____doc__,
"Struct(format)\n" "Struct(format)\n"
"--\n" "--\n"
"\n" "\n"
@ -20,13 +20,13 @@ PyDoc_STRVAR(Struct__doc__,
"\n" "\n"
"See help(struct) for more on format strings."); "See help(struct) for more on format strings.");
static PyObject * static int
Struct_impl(PyTypeObject *type, PyObject *format); Struct___init___impl(PyStructObject *self, PyObject *format);
static PyObject * static int
Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) Struct___init__(PyObject *self, PyObject *args, PyObject *kwargs)
{ {
PyObject *return_value = NULL; int return_value = -1;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1 #define NUM_KEYWORDS 1
@ -62,7 +62,7 @@ Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
} }
format = fastargs[0]; format = fastargs[0];
return_value = Struct_impl(type, format); return_value = Struct___init___impl((PyStructObject *)self, format);
exit: exit:
return return_value; return return_value;
@ -436,4 +436,4 @@ exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=6a20e87f9b298b14 input=a9049054013a1b77]*/ /*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/