]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-112358: Fix Python 3.12 regression with subclassing struct.Struct (GH-11242...
authorMark Dickinson <dickinsm@gmail.com>
Mon, 27 Nov 2023 08:25:06 +0000 (08:25 +0000)
committerGitHub <noreply@github.com>
Mon, 27 Nov 2023 08:25:06 +0000 (08:25 +0000)
* [3.12] gh-112358: Fix Python 3.12 regression with subclassing struct.Struct. (GH-112424)

Revert commit c8c0afc7137ab9f22bf59d591084948ca967c97c (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 9fe60340d7e8dc22b3aec205c557bc69a1b2d18c)

Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
* Remove unrelated test

Lib/test/test_struct.py
Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst [new file with mode: 0644]
Modules/_struct.c
Modules/clinic/_struct.c.h

index 6b1f22f66fd15758a574e327b36be7b4c4387446..8d4b13a618315f8436c9643c5f54d226e438fd74 100644 (file)
@@ -700,20 +700,6 @@ class StructTest(unittest.TestCase):
                 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.
@@ -774,6 +760,16 @@ class StructTest(unittest.TestCase):
         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).
diff --git a/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst
new file mode 100644 (file)
index 0000000..e473ded
--- /dev/null
@@ -0,0 +1,2 @@
+Revert change to :class:`struct.Struct` initialization that broke some cases
+of subclassing.
index 4f9478bd98095d5b714592de8836f0612ff93ec1..55efc0c6cfe948d420252c463427f3b634caa914 100644 (file)
@@ -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
 
@@ -1564,24 +1583,16 @@ 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);
@@ -1589,24 +1600,19 @@ Struct_impl(PyTypeObject *type, PyObject *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)
 {
@@ -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},
 };
index c3cf179ed4c040d62713e48ea81458663ea5963c..b21d9ff292433b01b4e8f7f30b4df2642976eec1 100644 (file)
@@ -8,7 +8,7 @@ preserve
 #endif
 
 
-PyDoc_STRVAR(Struct__doc__,
+PyDoc_STRVAR(Struct___init____doc__,
 "Struct(format)\n"
 "--\n"
 "\n"
@@ -19,13 +19,13 @@ PyDoc_STRVAR(Struct__doc__,
 "\n"
 "See help(struct) for more on format strings.");
 
-static PyObject *
-Struct_impl(PyTypeObject *type, PyObject *format);
+static int
+Struct___init___impl(PyStructObject *self, PyObject *format);
 
-static PyObject *
-Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+static int
+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)
 
     #define NUM_KEYWORDS 1
@@ -61,7 +61,7 @@ Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs)
         goto exit;
     }
     format = fastargs[0];
-    return_value = Struct_impl(type, format);
+    return_value = Struct___init___impl((PyStructObject *)self, format);
 
 exit:
     return return_value;
@@ -451,4 +451,4 @@ exit:
 
     return return_value;
 }
-/*[clinic end generated code: output=f3d6e06f80368998 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=eca7df0e75f8919d input=a9049054013a1b77]*/