Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
revert deprecation, treat as bugfix
  • Loading branch information
iritkatriel committed Nov 6, 2023
commit 4196b0a1fe0d21254e7f67e27aad5ca5f35af2fe
14 changes: 5 additions & 9 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1206,26 +1206,22 @@ by writing to f_lineno.

Frame objects support one method:

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

This method clears all references to local variables held by the
frame. Also, if the frame belonged to a generator, the generator
is finalized. This helps break reference cycles involving frame
objects (for example when catching an exception and storing its
traceback for later use).

: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.
:exc:`RuntimeError` is raised if the frame is currently executing
or suspended.

.. versionadded:: 3.4

.. versionchanged:: 3.13
Clearing a suspended frame is deprecated. Added the *raise_if_suspended*
argument.
Attempting to clear a suspended frame raises :exc:`RuntimeError`.


.. _traceback-objects:

Expand Down
3 changes: 2 additions & 1 deletion Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ 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.
* 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`.)


Expand Down
23 changes: 8 additions & 15 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import threading
import types
import unittest
import warnings
import weakref
try:
import _testcapi
Expand Down Expand Up @@ -84,17 +83,13 @@ def g():

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

# Clearing the frame closes the generator
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)
# 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 @@ -126,12 +121,10 @@ def g():
gen = g()
f = next(gen)
self.assertFalse(endly)
# Clearing the frame closes the generator
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=DeprecationWarning)
# Cannot clear a suspended frame
with self.assertRaisesRegex(RuntimeError, 'suspended frame'):
f.clear()

self.assertTrue(endly)
self.assertFalse(endly)

def test_lineno_with_tracing(self):
def record_line():
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deprecate clearing a suspended frame.
Raise exception if :meth:`frame.clear` is called on a suspended frame.
26 changes: 7 additions & 19 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -933,31 +933,15 @@ frame_tp_clear(PyFrameObject *f)
}

static PyObject *
frame_clear(PyFrameObject *f, PyObject *args, PyObject *kwds)
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{
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;
}
goto suspended;
}
_PyGen_Finalize((PyObject *)gen);
}
Expand All @@ -973,6 +957,10 @@ frame_clear(PyFrameObject *f, PyObject *args, PyObject *kwds)
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 Expand Up @@ -1002,7 +990,7 @@ frame_repr(PyFrameObject *f)
}

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