https://github.com/python/cpython/commit/398dbef62a2542644b2c1e31d25bcc61837671d5
commit: 398dbef62a2542644b2c1e31d25bcc61837671d5
branch: 3.15
author: Timofei Ivankov <[email protected]>
committer: hugovk <[email protected]>
date: 2026-08-04T13:03:28+03:00
summary:
[3.15] gh-154902: Type-check the SET_ADD operand (#155071)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst
M Lib/test/test_type_annotations.py
M Modules/_testinternalcapi/test_cases.c.h
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
diff --git a/Lib/test/test_type_annotations.py
b/Lib/test/test_type_annotations.py
index b751f825bb97d59..54db123a5dc2bf3 100644
--- a/Lib/test/test_type_annotations.py
+++ b/Lib/test/test_type_annotations.py
@@ -1,5 +1,6 @@
import annotationlib
import inspect
+import itertools
import textwrap
import types
import unittest
@@ -896,3 +897,18 @@ class Generic:
mod = build_module(code)
annos = mod.__annotations__
self.assertEqual(annos, {"annotated_name": 0})
+
+ # gh-154902
+ def test_conditional_annotations_rebound(self):
+ # user code can rebind __conditional_annotations__ to any object
+ lefts = ("__conditional_annotations__",
+ 'globals()["__conditional_annotations__"]')
+ values = ("0", "{}", "[]", "''", "object()", "frozenset()")
+ for left, value in itertools.product(lefts, values):
+ with self.subTest(left=left, value=value):
+ code = f"""
+ {left} = {value}
+ x: int
+ """
+ with self.assertRaises(TypeError):
+ run_code(code)
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst
new file mode 100644
index 000000000000000..49f34eaeddec4e4
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst
@@ -0,0 +1,2 @@
+Fix a crash when ``__conditional_annotations__`` is rebound to a non-set
+object.
diff --git a/Modules/_testinternalcapi/test_cases.c.h
b/Modules/_testinternalcapi/test_cases.c.h
index e19ff840458e0e0..c51ed3c812523a7 100644
--- a/Modules/_testinternalcapi/test_cases.c.h
+++ b/Modules/_testinternalcapi/test_cases.c.h
@@ -11424,8 +11424,21 @@
_PyStackRef v;
v = stack_pointer[-1];
set = stack_pointer[-2 - (oparg-1)];
+ PyObject *set_o = PyStackRef_AsPyObjectBorrow(set);
+ if (!PySet_CheckExact(set_o)) {
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%T' object is not a set", set_o);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ stack_pointer += -1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(v);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ JUMP_TO_LABEL(error);
+ }
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PySet_AddTakeRef((PySetObject
*)PyStackRef_AsPyObjectBorrow(set),
+ int err = _PySet_AddTakeRef((PySetObject *)set_o,
PyStackRef_AsPyObjectSteal(v));
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err) {
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index f39a5f0b07a5fcc..4f0d0227cc2eb04 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1378,7 +1378,15 @@ dummy_func(
}
inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) {
- int err = _PySet_AddTakeRef((PySetObject
*)PyStackRef_AsPyObjectBorrow(set),
+ PyObject *set_o = PyStackRef_AsPyObjectBorrow(set);
+ // gh-154902: user code can rebind __conditional_annotations__
+ if (!PySet_CheckExact(set_o)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%T' object is not a set", set_o);
+ PyStackRef_CLOSE(v);
+ ERROR_IF(true);
+ }
+ int err = _PySet_AddTakeRef((PySetObject *)set_o,
PyStackRef_AsPyObjectSteal(v));
ERROR_IF(err);
}
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 4d5a8ab6b8af0b9..7644c2d38d7f068 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -8212,11 +8212,28 @@
oparg = CURRENT_OPARG();
v = _stack_item_0;
set = stack_pointer[-1 - (oparg-1)];
+ PyObject *set_o = PyStackRef_AsPyObjectBorrow(set);
+ if (!PySet_CheckExact(set_o)) {
+ stack_pointer[0] = v;
+ stack_pointer += 1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%T' object is not a set", set_o);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ stack_pointer += -1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(v);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ SET_CURRENT_CACHED_VALUES(0);
+ JUMP_TO_ERROR();
+ }
stack_pointer[0] = v;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PySet_AddTakeRef((PySetObject
*)PyStackRef_AsPyObjectBorrow(set),
+ int err = _PySet_AddTakeRef((PySetObject *)set_o,
PyStackRef_AsPyObjectSteal(v));
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 512c358014fcffe..741306f7cf27f7c 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -11421,8 +11421,21 @@
_PyStackRef v;
v = stack_pointer[-1];
set = stack_pointer[-2 - (oparg-1)];
+ PyObject *set_o = PyStackRef_AsPyObjectBorrow(set);
+ if (!PySet_CheckExact(set_o)) {
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%T' object is not a set", set_o);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ stack_pointer += -1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(v);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ JUMP_TO_LABEL(error);
+ }
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PySet_AddTakeRef((PySetObject
*)PyStackRef_AsPyObjectBorrow(set),
+ int err = _PySet_AddTakeRef((PySetObject *)set_o,
PyStackRef_AsPyObjectSteal(v));
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err) {
_______________________________________________
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]