diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index c4fcc1993ca627..aeb4de6caa527d 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1155,6 +1155,24 @@ class D(dict): pass {'a':1, 'b':2} ) + def test_store_attr_with_hint(self): + # gh-133441: Regression test for STORE_ATTR_WITH_HINT bytecode + class Node: + def __init__(self): + self.parents = {} + + def __setstate__(self, data_dict): + self.__dict__ = data_dict + self.parents = {} + + class Dict(dict): + pass + + obj = Node() + obj.__setstate__({'parents': {}}) + obj.__setstate__({'parents': {}}) + obj.__setstate__(Dict({'parents': {}})) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst new file mode 100644 index 00000000000000..4e893045b1d60e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst @@ -0,0 +1,2 @@ +Fix crash upon setting an attribute with a :class:`dict` subclass. +Patch by Victor Stinner. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6d5a42943b0d98..1da434bbbc892a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2149,7 +2149,8 @@ dummy_func( assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL); - assert(PyDict_CheckExact((PyObject *)dict)); + DEOPT_IF(!PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); PyObject *old_value; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9665774cf9c03c..be00bf6eb6a39e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5591,7 +5591,7 @@ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL, STORE_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); + DEOPT_IF(!PyDict_CheckExact((PyObject *)dict), STORE_ATTR); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); PyObject *old_value;