Skip to content

Commit

Permalink
pythongh-79932: deprecate clearing a suspended frame and add option t…
Browse files Browse the repository at this point in the history
…o get exception.
  • Loading branch information
iritkatriel committed Nov 6, 2023
1 parent 5e5762a commit af0b965
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 5 deletions.
10 changes: 9 additions & 1 deletion Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ by writing to f_lineno.

Frame objects support one method:

.. method:: frame.clear()
.. method:: frame.clear([raise_if_suspended])

This method clears all references to local variables held by the
frame. Also, if the frame belonged to a generator, the generator
Expand All @@ -1216,8 +1216,16 @@ Frame objects support one method:

:exc:`RuntimeError` is raised if the frame is currently executing.

Clearing a suspended frame is deprecated.
The optional argument *raise_if_suspended* can be passed ``True`` to
make this function raise a :exc:`RuntimeError` instead of issuing a
deprecation warning if the frame is suspended.

.. versionadded:: 3.4

.. versionchanged:: 3.13
Clearing a suspended frame is deprecated. Added the *raise_if_suspended*
argument.

.. _traceback-objects:

Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ 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 is deprecated.
(Contributed by Irit Katriel in :gh:`79932`.)


Pending Removal in Python 3.14
------------------------------
Expand Down
19 changes: 17 additions & 2 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import threading
import types
import unittest
import warnings
import weakref
try:
import _testcapi
Expand Down Expand Up @@ -80,8 +81,19 @@ def g():
gen = g()
next(gen)
self.assertFalse(endly)

# Raise exception when attempting to clear a suspended frame
with self.assertRaisesRegex(RuntimeError, r'suspended frame'):
gen.gi_frame.clear(True)
self.assertFalse(endly)

# Clearing the frame closes the generator
gen.gi_frame.clear()
try:
with self.assertWarnsRegex(DeprecationWarning, r'suspended frame'):
gen.gi_frame.clear()
except DeprecationWarning:
# Suppress the warning when running with -We
pass
self.assertTrue(endly)

def test_clear_executing(self):
Expand Down Expand Up @@ -115,7 +127,10 @@ def g():
f = next(gen)
self.assertFalse(endly)
# Clearing the frame closes the generator
f.clear()
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=DeprecationWarning)
f.clear()

self.assertTrue(endly)

def test_lineno_with_tracing(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate clearing a suspended frame.
23 changes: 21 additions & 2 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -933,13 +933,32 @@ frame_tp_clear(PyFrameObject *f)
}

static PyObject *
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
frame_clear(PyFrameObject *f, PyObject *args, PyObject *kwds)
{
bool raise_if_suspended = false;
PyObject *v = NULL;
if (!PyArg_UnpackTuple(args, "clear", 0, 1, &v)) {
return NULL;
}
if (v != NULL && PyObject_IsTrue(v)) {
raise_if_suspended = true;
}

if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) {
PyGenObject *gen = _PyFrame_GetGenerator(f->f_frame);
if (gen->gi_frame_state == FRAME_EXECUTING) {
goto running;
}
if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
if (raise_if_suspended) {
PyErr_SetString(PyExc_RuntimeError, "cannot clear a suspended frame");
return NULL;
}
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"clearing a suspended frame is deprecated", 1) < 0) {
return NULL;
}
}
_PyGen_Finalize((PyObject *)gen);
}
else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) {
Expand Down Expand Up @@ -983,7 +1002,7 @@ frame_repr(PyFrameObject *f)
}

static PyMethodDef frame_methods[] = {
{"clear", (PyCFunction)frame_clear, METH_NOARGS,
{"clear", (PyCFunction)frame_clear, METH_VARARGS,
clear__doc__},
{"__sizeof__", (PyCFunction)frame_sizeof, METH_NOARGS,
sizeof__doc__},
Expand Down

0 comments on commit af0b965

Please sign in to comment.