]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112358: Fix Python 3.12 regression with subclassing struct.Struct. (#112424)
authorMark Dickinson <dickinsm@gmail.com>
Sun, 26 Nov 2023 14:29:52 +0000 (14:29 +0000)
committerGitHub <noreply@github.com>
Sun, 26 Nov 2023 14:29:52 +0000 (14:29 +0000)
Revert commit c8c0afc7137ab9f22bf59d591084948ca967c97c (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`.

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 c76649cdcd9cce2bf19559140242ed09757d0fc3..15f6ee06ffe19b603ef122f8200cced905f4aad5 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,15 @@ 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')
+
     def test_repr(self):
         s = struct.Struct('=i2H')
         self.assertEqual(repr(s), f'Struct({s.format!r})')
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 0116b03ea9511550c26c3bf4f33e277c5fedbcc3..24a4cb3b6413f1ab97e2cf8194d8daad69d5689d 100644 (file)
@@ -1553,9 +1553,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
 
@@ -1567,24 +1586,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);
@@ -1592,24 +1603,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)
 {
@@ -2219,8 +2225,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 e5118fbdb3b9d3db0bec55a0134e4f389cb03fae..1a07532bdd75ad50be2d36e3ea19c0a5c6c193ff 100644 (file)
@@ -9,7 +9,7 @@ preserve
 #include "pycore_abstract.h"      // _PyNumber_Index()
 #include "pycore_modsupport.h"    // _PyArg_UnpackKeywords()
 
-PyDoc_STRVAR(Struct__doc__,
+PyDoc_STRVAR(Struct___init____doc__,
 "Struct(format)\n"
 "--\n"
 "\n"
@@ -20,13 +20,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
@@ -62,7 +62,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;
@@ -436,4 +436,4 @@ exit:
 
     return return_value;
 }
-/*[clinic end generated code: output=6a20e87f9b298b14 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/