https://github.com/python/cpython/commit/515ae4078dffa0b74e5e5431462c2f4fe4563ffa
commit: 515ae4078dffa0b74e5e5431462c2f4fe4563ffa
branch: main
author: Sergey B Kirpichev <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-11T15:52:01Z
summary:
gh-78724: Raise RuntimeError's when calling methods on non-ready Struct()'s
(GH-143643)
files:
A Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst
M Lib/test/test_struct.py
M Modules/_struct.c
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index bbfe19a4e0bab7..88662fec60fe4a 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -816,6 +816,18 @@ def test_endian_table_init_subinterpreters(self):
results = executor.map(exec, [code] * 5)
self.assertListEqual(list(results), [None] * 5)
+ def test_operations_on_half_initialized_Struct(self):
+ S = struct.Struct.__new__(struct.Struct)
+
+ spam = array.array('b', b' ')
+ self.assertRaises(RuntimeError, S.iter_unpack, spam)
+ self.assertRaises(RuntimeError, S.pack, 1)
+ self.assertRaises(RuntimeError, S.pack_into, spam, 1)
+ self.assertRaises(RuntimeError, S.unpack, spam)
+ self.assertRaises(RuntimeError, S.unpack_from, spam)
+ self.assertRaises(RuntimeError, getattr, S, 'format')
+ self.assertEqual(S.size, -1)
+
class UnpackIteratorTest(unittest.TestCase):
"""
diff --git
a/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst
b/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst
new file mode 100644
index 00000000000000..8a4bec4e1653d1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst
@@ -0,0 +1,3 @@
+Raise :exc:`RuntimeError`'s when user attempts to call methods on
+half-initialized :class:`~struct.Struct` objects, For example, created by
+``Struct.__new__(Struct)``. Patch by Sergey B Kirpichev.
diff --git a/Modules/_struct.c b/Modules/_struct.c
index 2acb3df3a30395..a8e9021f0a303a 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -1698,8 +1698,6 @@ prepare_s(PyStructObject *self)
return -1;
}
- self->s_size = size;
- self->s_len = len;
codes = PyMem_Malloc((ncodes + 1) * sizeof(formatcode));
if (codes == NULL) {
PyErr_NoMemory();
@@ -1709,6 +1707,8 @@ prepare_s(PyStructObject *self)
if (self->s_codes != NULL)
PyMem_Free(self->s_codes);
self->s_codes = codes;
+ self->s_size = size;
+ self->s_len = len;
s = fmt;
size = 0;
@@ -1897,6 +1897,14 @@ s_unpack_internal(PyStructObject *soself, const char
*startfrom,
return NULL;
}
+#define ENSURE_STRUCT_IS_READY(self) \
+ do { \
+ if (!(self)->s_codes) { \
+ PyErr_SetString(PyExc_RuntimeError, \
+ "Struct object is not initialized"); \
+ return NULL; \
+ } \
+ } while (0);
/*[clinic input]
Struct.unpack
@@ -1917,7 +1925,7 @@ Struct_unpack_impl(PyStructObject *self, Py_buffer
*buffer)
/*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/
{
_structmodulestate *state = get_struct_state_structinst(self);
- assert(self->s_codes != NULL);
+ ENSURE_STRUCT_IS_READY(self);
if (buffer->len != self->s_size) {
PyErr_Format(state->StructError,
"unpack requires a buffer of %zd bytes",
@@ -1949,7 +1957,7 @@ Struct_unpack_from_impl(PyStructObject *self, Py_buffer
*buffer,
/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/
{
_structmodulestate *state = get_struct_state_structinst(self);
- assert(self->s_codes != NULL);
+ ENSURE_STRUCT_IS_READY(self);
if (offset < 0) {
if (offset + self->s_size > 0) {
@@ -2101,8 +2109,7 @@ Struct_iter_unpack_impl(PyStructObject *self, PyObject
*buffer)
{
_structmodulestate *state = get_struct_state_structinst(self);
unpackiterobject *iter;
-
- assert(self->s_codes != NULL);
+ ENSURE_STRUCT_IS_READY(self);
if (self->s_size == 0) {
PyErr_Format(state->StructError,
@@ -2242,8 +2249,8 @@ s_pack(PyObject *self, PyObject *const *args, Py_ssize_t
nargs)
/* Validate arguments. */
soself = PyStructObject_CAST(self);
+ ENSURE_STRUCT_IS_READY(soself);
assert(PyStruct_Check(self, state));
- assert(soself->s_codes != NULL);
if (nargs != soself->s_len)
{
PyErr_Format(state->StructError,
@@ -2285,8 +2292,8 @@ s_pack_into(PyObject *self, PyObject *const *args,
Py_ssize_t nargs)
/* Validate arguments. +1 is for the first arg as buffer. */
soself = PyStructObject_CAST(self);
+ ENSURE_STRUCT_IS_READY(soself);
assert(PyStruct_Check(self, state));
- assert(soself->s_codes != NULL);
if (nargs != (soself->s_len + 2))
{
if (nargs == 0) {
@@ -2373,6 +2380,7 @@ static PyObject *
s_get_format(PyObject *op, void *Py_UNUSED(closure))
{
PyStructObject *self = PyStructObject_CAST(op);
+ ENSURE_STRUCT_IS_READY(self);
return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format),
PyBytes_GET_SIZE(self->s_format));
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]