Skip to content

Commit 7697820

Browse files
committed
issue DeprecationWarning for implicit tp_new calls in subclasses
This catch current pattern for Struct's subclassing like class MyStruct(Struct): def __init__(self): super().__init__('>h')
1 parent 26cde89 commit 7697820

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

Lib/test/test_struct.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,22 @@ def __new__(cls, arg):
817817
my_struct = MyStruct(5)
818818
self.assertEqual(my_struct.pack(123), b'\x00{')
819819

820+
class MyStruct(struct.Struct):
821+
def __init__(self, *args, **kwargs):
822+
super().__init__('>h')
823+
824+
with self.assertWarns(DeprecationWarning):
825+
my_struct = MyStruct('<h')
826+
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
827+
828+
with self.assertWarns(DeprecationWarning):
829+
my_struct = MyStruct(5)
830+
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
831+
832+
with self.assertWarns(DeprecationWarning):
833+
my_struct = MyStruct('>h')
834+
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
835+
820836
def test_repr(self):
821837
s = struct.Struct('=i2H')
822838
self.assertEqual(repr(s), f'Struct({s.format!r})')

Modules/_struct.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,7 +1796,11 @@ s_create(PyStructObject *self, PyObject *format)
17961796
static PyObject *
17971797
s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
17981798
{
1799+
PyObject *mod = PyType_GetModuleByDef(type, &_structmodule);
1800+
_structmodulestate *state = PyModule_GetState(mod);
17991801
PyStructObject *self;
1802+
bool implicit_new_call = s_new == PyType_GetSlot(type, Py_tp_new);
1803+
bool in_subclass = type != (PyTypeObject *)state->PyStructType;
18001804

18011805
assert(type != NULL);
18021806
allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc);
@@ -1817,8 +1821,18 @@ s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
18171821
}
18181822
}
18191823
else {
1820-
if (s_create(self, PyTuple_GET_ITEM(args, 0))) {
1821-
goto err;
1824+
if (implicit_new_call && in_subclass) {
1825+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
1826+
("Creation of half-initialized Struct() objects"
1827+
" is deprecated, use Struct.__new__(cls, format)"), 1))
1828+
{
1829+
goto err;
1830+
}
1831+
}
1832+
else {
1833+
if (s_create(self, PyTuple_GET_ITEM(args, 0))) {
1834+
goto err;
1835+
}
18221836
}
18231837
}
18241838
return (PyObject *)self;
@@ -1843,6 +1857,9 @@ static int
18431857
Struct___init___impl(PyStructObject *self, PyObject *format)
18441858
/*[clinic end generated code: output=b8e80862444e92d0 input=dcf0b5a00eb0dbd9]*/
18451859
{
1860+
bool explicit_init_call = (Struct___init__
1861+
!= PyType_GetSlot(Py_TYPE(self), Py_tp_init));
1862+
18461863
if (!format && !self->s_codes) {
18471864
PyErr_SetString(PyExc_TypeError,
18481865
"Struct() missing required argument 'format' (pos 1)");
@@ -1859,7 +1876,7 @@ Struct___init___impl(PyStructObject *self, PyObject *format)
18591876
return s_create(self, format);
18601877
}
18611878
else {
1862-
if (!self->s_codes && s_create(self, format)) {
1879+
if (explicit_init_call && s_create(self, format)) {
18631880
return -1;
18641881
}
18651882
self->init_called = true;

0 commit comments

Comments
 (0)