https://github.com/python/cpython/commit/e178a13f2f79aa8c1cd9c940fb1962c65cee3e24
commit: e178a13f2f79aa8c1cd9c940fb1962c65cee3e24
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-21T16:00:39Z
summary:
[3.13] gh-156196: Fix a failed assignment to a PyMemberDef attribute changing
its value (GH-156197)
PyMember_SetOne() stored the result of the conversion before checking it
for an error.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-18-00-00.gh-issue-156196.Mv7kBq.rst
M Lib/test/test_capi/test_structmembers.py
M Python/structmember.c
diff --git a/Lib/test/test_capi/test_structmembers.py
b/Lib/test/test_capi/test_structmembers.py
index 08ca1f828529cf2..0a4e4d935658155 100644
--- a/Lib/test/test_capi/test_structmembers.py
+++ b/Lib/test/test_capi/test_structmembers.py
@@ -60,7 +60,10 @@ def _test_warn(self, name, value, expected=None):
def _test_overflow(self, name, value):
ts = self.ts
+ oldvalue = getattr(ts, name)
self.assertRaises(OverflowError, setattr, ts, name, value)
+ # a failed assignment does not change the value
+ self.assertEqual(getattr(ts, name), oldvalue)
def _test_int_range(self, name, minval, maxval, *, hardlimit=None,
indexlimit=None):
@@ -154,8 +157,11 @@ def test_bad_assignments(self):
# issue8014: this produced 'bad argument to internal function'
# internal error
for nonint in None, 3.2j, "full of eels", {}, []:
- for attr in integer_attributes:
+ for attr in integer_attributes + ['T_FLOAT', 'T_DOUBLE']:
+ oldvalue = getattr(ts, attr)
self.assertRaises(TypeError, setattr, ts, attr, nonint)
+ # a failed assignment does not change the value
+ self.assertEqual(getattr(ts, attr), oldvalue)
def test_inplace_string(self):
ts = self.ts
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-18-00-00.gh-issue-156196.Mv7kBq.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-18-00-00.gh-issue-156196.Mv7kBq.rst
new file mode 100644
index 000000000000000..65d824884eccaaf
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-18-00-00.gh-issue-156196.Mv7kBq.rst
@@ -0,0 +1,3 @@
+A failed assignment to an attribute defined with :c:type:`PyMemberDef` of
+type ``Py_T_LONG``, ``Py_T_LONGLONG``, ``Py_T_PYSSIZET`` or ``Py_T_DOUBLE``
+no longer changes its value.
diff --git a/Python/structmember.c b/Python/structmember.c
index d5e7ab83093dc86..58caa03c31d3029 100644
--- a/Python/structmember.c
+++ b/Python/structmember.c
@@ -251,9 +251,10 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
break;
}
case Py_T_LONG:{
- *(long*)addr = PyLong_AsLong(v);
- if ((*(long*)addr == -1) && PyErr_Occurred())
+ long long_val = PyLong_AsLong(v);
+ if ((long_val == -1) && PyErr_Occurred())
return -1;
+ *(long*)addr = long_val;
break;
}
case Py_T_ULONG: {
@@ -283,10 +284,10 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
break;
}
case Py_T_PYSSIZET:{
- *(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
- if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)
- && PyErr_Occurred())
- return -1;
+ Py_ssize_t ssize_val = PyLong_AsSsize_t(v);
+ if ((ssize_val == (Py_ssize_t)-1) && PyErr_Occurred())
+ return -1;
+ *(Py_ssize_t*)addr = ssize_val;
break;
}
case Py_T_FLOAT:{
@@ -296,11 +297,13 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
*(float*)addr = (float)double_val;
break;
}
- case Py_T_DOUBLE:
- *(double*)addr = PyFloat_AsDouble(v);
- if ((*(double*)addr == -1) && PyErr_Occurred())
+ case Py_T_DOUBLE:{
+ double double_val = PyFloat_AsDouble(v);
+ if ((double_val == -1) && PyErr_Occurred())
return -1;
+ *(double*)addr = double_val;
break;
+ }
case _Py_T_OBJECT:
case Py_T_OBJECT_EX:
Py_BEGIN_CRITICAL_SECTION(obj);
@@ -326,10 +329,10 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
PyErr_SetString(PyExc_TypeError, "readonly attribute");
return -1;
case Py_T_LONGLONG:{
- long long value;
- *(long long*)addr = value = PyLong_AsLongLong(v);
+ long long value = PyLong_AsLongLong(v);
if ((value == -1) && PyErr_Occurred())
return -1;
+ *(long long*)addr = value;
break;
}
case Py_T_ULONGLONG: {
_______________________________________________
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]