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

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`.
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index c76649c..15f6ee0 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -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.
@@ -774,6 +760,15 @@ 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')
+
     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
index 0000000..e473ded
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst
@@ -0,0 +1,2 @@
+Revert change to :class:`struct.Struct` initialization that broke some cases
+of subclassing.
diff --git a/Modules/_struct.c b/Modules/_struct.c
index 0116b03..24a4cb3 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -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},
 };
diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h
index e5118fb..1a07532 100644
--- a/Modules/clinic/_struct.c.h
+++ b/Modules/clinic/_struct.c.h
@@ -9,7 +9,7 @@
 #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 @@
 "\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 @@
         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 @@
 
     return return_value;
 }
-/*[clinic end generated code: output=6a20e87f9b298b14 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/