gh-79932: raise exception if frame.clear() is called on a suspended frame (#111792)

This commit is contained in:
Irit Katriel 2023-11-07 08:49:30 +00:00 committed by GitHub
parent d2ddfccfb4
commit 13405ecffd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 27 additions and 7 deletions

View File

@ -1214,10 +1214,15 @@ Frame objects support one method:
objects (for example when catching an exception and storing its objects (for example when catching an exception and storing its
traceback for later use). traceback for later use).
:exc:`RuntimeError` is raised if the frame is currently executing. :exc:`RuntimeError` is raised if the frame is currently executing
or suspended.
.. versionadded:: 3.4 .. versionadded:: 3.4
.. versionchanged:: 3.13
Attempting to clear a suspended frame raises :exc:`RuntimeError`
(as has always been the case for executing frames).
.. _traceback-objects: .. _traceback-objects:

View File

@ -397,6 +397,10 @@ Deprecated
and methods that consider plural forms even if the translation was not found. and methods that consider plural forms even if the translation was not found.
(Contributed by Serhiy Storchaka in :gh:`88434`.) (Contributed by Serhiy Storchaka in :gh:`88434`.)
* Calling :meth:`frame.clear` on a suspended frame raises :exc:`RuntimeError`
(as has always been the case for an executing frame).
(Contributed by Irit Katriel in :gh:`79932`.)
Pending Removal in Python 3.14 Pending Removal in Python 3.14
------------------------------ ------------------------------

View File

@ -80,9 +80,11 @@ class ClearTest(unittest.TestCase):
gen = g() gen = g()
next(gen) next(gen)
self.assertFalse(endly) self.assertFalse(endly)
# Clearing the frame closes the generator
gen.gi_frame.clear() # Cannot clear a suspended frame
self.assertTrue(endly) with self.assertRaisesRegex(RuntimeError, r'suspended frame'):
gen.gi_frame.clear()
self.assertFalse(endly)
def test_clear_executing(self): def test_clear_executing(self):
# Attempting to clear an executing frame is forbidden. # Attempting to clear an executing frame is forbidden.
@ -114,9 +116,10 @@ class ClearTest(unittest.TestCase):
gen = g() gen = g()
f = next(gen) f = next(gen)
self.assertFalse(endly) self.assertFalse(endly)
# Clearing the frame closes the generator # Cannot clear a suspended frame
f.clear() with self.assertRaisesRegex(RuntimeError, 'suspended frame'):
self.assertTrue(endly) f.clear()
self.assertFalse(endly)
def test_lineno_with_tracing(self): def test_lineno_with_tracing(self):
def record_line(): def record_line():

View File

@ -0,0 +1 @@
Raise exception if :meth:`frame.clear` is called on a suspended frame.

View File

@ -937,6 +937,9 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
if (gen->gi_frame_state == FRAME_EXECUTING) { if (gen->gi_frame_state == FRAME_EXECUTING) {
goto running; goto running;
} }
if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
goto suspended;
}
_PyGen_Finalize((PyObject *)gen); _PyGen_Finalize((PyObject *)gen);
} }
else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) { else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) {
@ -951,6 +954,10 @@ running:
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"cannot clear an executing frame"); "cannot clear an executing frame");
return NULL; return NULL;
suspended:
PyErr_SetString(PyExc_RuntimeError,
"cannot clear a suspended frame");
return NULL;
} }
PyDoc_STRVAR(clear__doc__, PyDoc_STRVAR(clear__doc__,