Skip to content

Commit

Permalink
pythongh-79932: raise exception if frame.clear() is called on a suspe…
Browse files Browse the repository at this point in the history
…nded frame (python#111792)
  • Loading branch information
iritkatriel authored Nov 7, 2023
1 parent d2ddfcc commit 13405ec
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 7 deletions.
7 changes: 6 additions & 1 deletion Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1214,10 +1214,15 @@ Frame objects support one method:
objects (for example when catching an exception and storing its
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

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


.. _traceback-objects:

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,10 @@ Deprecated
and methods that consider plural forms even if the translation was not found.
(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
------------------------------
Expand Down
15 changes: 9 additions & 6 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ def g():
gen = g()
next(gen)
self.assertFalse(endly)
# Clearing the frame closes the generator
gen.gi_frame.clear()
self.assertTrue(endly)

# Cannot clear a suspended frame
with self.assertRaisesRegex(RuntimeError, r'suspended frame'):
gen.gi_frame.clear()
self.assertFalse(endly)

def test_clear_executing(self):
# Attempting to clear an executing frame is forbidden.
Expand Down Expand Up @@ -114,9 +116,10 @@ def g():
gen = g()
f = next(gen)
self.assertFalse(endly)
# Clearing the frame closes the generator
f.clear()
self.assertTrue(endly)
# Cannot clear a suspended frame
with self.assertRaisesRegex(RuntimeError, 'suspended frame'):
f.clear()
self.assertFalse(endly)

def test_lineno_with_tracing(self):
def record_line():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Raise exception if :meth:`frame.clear` is called on a suspended frame.
7 changes: 7 additions & 0 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,9 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
if (gen->gi_frame_state == FRAME_EXECUTING) {
goto running;
}
if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
goto suspended;
}
_PyGen_Finalize((PyObject *)gen);
}
else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) {
Expand All @@ -951,6 +954,10 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
PyErr_SetString(PyExc_RuntimeError,
"cannot clear an executing frame");
return NULL;
suspended:
PyErr_SetString(PyExc_RuntimeError,
"cannot clear a suspended frame");
return NULL;
}

PyDoc_STRVAR(clear__doc__,
Expand Down

0 comments on commit 13405ec

Please sign in to comment.