Skip to content

Commit 024393d

Browse files
iritkatrielGlyphack
authored andcommitted
pythongh-79932: raise exception if frame.clear() is called on a suspended frame (python#111792)
1 parent 97a8ae1 commit 024393d

File tree

5 files changed

+27
-7
lines changed

5 files changed

+27
-7
lines changed

Doc/reference/datamodel.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -1214,10 +1214,15 @@ Frame objects support one method:
12141214
objects (for example when catching an exception and storing its
12151215
traceback for later use).
12161216

1217-
:exc:`RuntimeError` is raised if the frame is currently executing.
1217+
:exc:`RuntimeError` is raised if the frame is currently executing
1218+
or suspended.
12181219

12191220
.. versionadded:: 3.4
12201221

1222+
.. versionchanged:: 3.13
1223+
Attempting to clear a suspended frame raises :exc:`RuntimeError`
1224+
(as has always been the case for executing frames).
1225+
12211226

12221227
.. _traceback-objects:
12231228

Doc/whatsnew/3.13.rst

+4
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,10 @@ Deprecated
397397
and methods that consider plural forms even if the translation was not found.
398398
(Contributed by Serhiy Storchaka in :gh:`88434`.)
399399

400+
* Calling :meth:`frame.clear` on a suspended frame raises :exc:`RuntimeError`
401+
(as has always been the case for an executing frame).
402+
(Contributed by Irit Katriel in :gh:`79932`.)
403+
400404

401405
Pending Removal in Python 3.14
402406
------------------------------

Lib/test/test_frame.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,11 @@ def g():
8080
gen = g()
8181
next(gen)
8282
self.assertFalse(endly)
83-
# Clearing the frame closes the generator
84-
gen.gi_frame.clear()
85-
self.assertTrue(endly)
83+
84+
# Cannot clear a suspended frame
85+
with self.assertRaisesRegex(RuntimeError, r'suspended frame'):
86+
gen.gi_frame.clear()
87+
self.assertFalse(endly)
8688

8789
def test_clear_executing(self):
8890
# Attempting to clear an executing frame is forbidden.
@@ -114,9 +116,10 @@ def g():
114116
gen = g()
115117
f = next(gen)
116118
self.assertFalse(endly)
117-
# Clearing the frame closes the generator
118-
f.clear()
119-
self.assertTrue(endly)
119+
# Cannot clear a suspended frame
120+
with self.assertRaisesRegex(RuntimeError, 'suspended frame'):
121+
f.clear()
122+
self.assertFalse(endly)
120123

121124
def test_lineno_with_tracing(self):
122125
def record_line():
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raise exception if :meth:`frame.clear` is called on a suspended frame.

Objects/frameobject.c

+7
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,9 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
937937
if (gen->gi_frame_state == FRAME_EXECUTING) {
938938
goto running;
939939
}
940+
if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
941+
goto suspended;
942+
}
940943
_PyGen_Finalize((PyObject *)gen);
941944
}
942945
else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) {
@@ -951,6 +954,10 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
951954
PyErr_SetString(PyExc_RuntimeError,
952955
"cannot clear an executing frame");
953956
return NULL;
957+
suspended:
958+
PyErr_SetString(PyExc_RuntimeError,
959+
"cannot clear a suspended frame");
960+
return NULL;
954961
}
955962

956963
PyDoc_STRVAR(clear__doc__,

0 commit comments

Comments
 (0)