Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fff147f
gh-78724: deprecate incomplete initialization of struct.Struct()
skirpichev Jan 10, 2026
d2a0345
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Jan 12, 2026
589fbc7
address review: test_Struct_reinitialization()
skirpichev Jan 12, 2026
5e91e87
catch new warning in test_operations_on_half_initialized_Struct()
skirpichev Jan 12, 2026
babb274
add init_called flag
skirpichev Jan 12, 2026
0e4e84b
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Jan 13, 2026
3f36635
a hack to support new idiom for subclassing
skirpichev Jan 13, 2026
e576475
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Jan 17, 2026
628aadd
+ filter out Struct signature test
skirpichev Jan 17, 2026
979cc18
Update Modules/_struct.c
skirpichev Jan 19, 2026
f7492ec
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Jan 19, 2026
db8f5f2
address review: reformat s_new()
skirpichev Jan 19, 2026
dc8cbef
address review: actual___init___impl -> s_init
skirpichev Jan 19, 2026
6206ae1
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Feb 22, 2026
12143d1
Update Doc/deprecations/pending-removal-in-3.20.rst
skirpichev Feb 25, 2026
d4ca6b8
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Feb 25, 2026
6b9b4fb
+ rename news
skirpichev Feb 25, 2026
79961a2
Apply suggestions from code review
skirpichev Feb 25, 2026
58550c0
address review: add test
skirpichev Feb 25, 2026
c815882
address review: fix for format
skirpichev Feb 25, 2026
f1388ee
address review: reformat if blocks
skirpichev Feb 25, 2026
685a6c4
address review: s_new
skirpichev Feb 25, 2026
76f8723
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Feb 26, 2026
538d301
address review: add fake signature for Struct
skirpichev Feb 26, 2026
09d6ebd
+test new idiom
skirpichev Feb 27, 2026
7f0a133
Some cleanup; also add reference to issue and reverted PR
skirpichev Feb 28, 2026
26cde89
Merge branch 'master' into deprecate-struct-init-2/78724
skirpichev Feb 28, 2026
7697820
issue DeprecationWarning for implicit tp_new calls in subclasses
skirpichev Feb 28, 2026
05eb292
Fix more corner cases.
serhiy-storchaka Mar 6, 2026
d9c0488
Update credicts.
serhiy-storchaka Mar 7, 2026
3de045b
Fix format for the __sizeof__ test.
serhiy-storchaka Mar 7, 2026
ebcf358
Fix test_Struct_reinitialization on big-endian and add tests for inva…
serhiy-storchaka Mar 9, 2026
e003dab
Add assertions for format.
serhiy-storchaka Mar 9, 2026
d453990
Factorize some tests.
serhiy-storchaka Mar 10, 2026
66dbc04
Merge branch 'main' into deprecate-struct-init
serhiy-storchaka Mar 10, 2026
6d60b16
Update Doc/deprecations/pending-removal-in-3.20.rst
serhiy-storchaka Mar 11, 2026
d7da0f8
Merge branch 'main' into deprecate-struct-init
serhiy-storchaka Mar 11, 2026
ca960a8
Emit FutureWarning instead of DeprecationWarning for re-initialization.
serhiy-storchaka Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add init_called flag
  • Loading branch information
skirpichev committed Jan 12, 2026
commit babb274bfe004bdaed46c2437a5e1ab25aa1842a
2 changes: 1 addition & 1 deletion Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ def test_Struct_reinitialization(self):

def check_sizeof(self, format_str, number_of_codes):
# The size of 'PyStructObject'
totalsize = support.calcobjsize('2n3P')
totalsize = support.calcobjsize('2n3P1?')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
totalsize = support.calcobjsize('2n3P1?')
totalsize = support.calcobjsize('2n3P?')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be '2n3P?0P', with padding.

# The size taken up by the 'formatcode' dynamic array
totalsize += struct.calcsize('P3n0P') * (number_of_codes + 1)
support.check_sizeof(self, struct.Struct(format_str), totalsize)
Expand Down
90 changes: 52 additions & 38 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ typedef struct {
formatcode *s_codes;
PyObject *s_format;
PyObject *weakreflist; /* List of weak references */
bool init_called;
} PyStructObject;

#define PyStructObject_CAST(op) ((PyStructObject *)(op))
Expand Down Expand Up @@ -1757,30 +1758,61 @@ prepare_s(PyStructObject *self)
return -1;
}

static int
actual___init___impl(PyStructObject *self, PyObject *format)
{
if (PyUnicode_Check(format)) {
format = PyUnicode_AsASCIIString(format);
if (format == NULL)
return -1;
}
else {
Py_INCREF(format);
}
if (!PyBytes_Check(format)) {
Py_DECREF(format);
PyErr_Format(PyExc_TypeError,
"Struct() argument 1 must be a str or bytes object, "
"not %.200s",
_PyType_Name(Py_TYPE(format)));
return -1;
}
Py_SETREF(self->s_format, format);
if (prepare_s(self)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of restoring s_format to its previous value if prepare_s() fails?

return -1;
}
return 0;
}

static PyObject *
s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *self;
PyStructObject *self;

if (PyTuple_GET_SIZE(args) != 1
if ((PyTuple_GET_SIZE(args) != 1 || kwds)
&& PyErr_WarnEx(PyExc_DeprecationWarning,
"Struct().__new__() has one required argument", 1))
"Struct.__new__() has one positional argument", 1))
{
return NULL;
}
assert(type != NULL);
allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc);
assert(alloc_func != NULL);

self = alloc_func(type, 0);
self = (PyStructObject *)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;
self->s_format = Py_NewRef(Py_None);
self->s_codes = NULL;
self->s_size = -1;
self->s_len = -1;
self->init_called = false;
if (PyTuple_GET_SIZE(args) > 0) {
if (actual___init___impl(self, PyTuple_GET_ITEM(args, 0))) {
Py_DECREF(self);
return NULL;
}
}
}
return self;
return (PyObject *)self;
}

/*[clinic input]
Expand All @@ -1800,37 +1832,21 @@ static int
Struct___init___impl(PyStructObject *self, PyObject *format)
/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/
{
int ret = 0;

if (self->s_codes
if (!self->init_called) {
if (!self->s_codes && actual___init___impl(self, format)) {
return -1;
}
self->init_called = true;
return 0;
}
if ((self->s_codes && self->init_called)
&& PyErr_WarnEx(PyExc_DeprecationWarning,
("Explicit call of __init__() on "
"initialized Struct() is deprecated"), 1))
{
return -1;
}
if (PyUnicode_Check(format)) {
format = PyUnicode_AsASCIIString(format);
if (format == NULL)
return -1;
}
else {
Py_INCREF(format);
}

if (!PyBytes_Check(format)) {
Py_DECREF(format);
PyErr_Format(PyExc_TypeError,
"Struct() argument 1 must be a str or bytes object, "
"not %.200s",
_PyType_Name(Py_TYPE(format)));
return -1;
}

Py_SETREF(self->s_format, format);

ret = prepare_s(self);
return ret;
return actual___init___impl(self, format);
}

static int
Expand Down Expand Up @@ -2473,9 +2489,7 @@ static PyType_Slot PyStructType_slots[] = {
{Py_tp_members, s_members},
{Py_tp_getset, s_getsetlist},
{Py_tp_init, Struct___init__},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, s_new},
{Py_tp_free, PyObject_GC_Del},
{0, 0},
};

Expand Down
Loading