Skip to content

Commit 7b3bc95

Browse files
authored
[3.11] GH-102818: Do not call PyTraceBack_Here in sys.settrace trampoline (GH-104650)
Backport of GH-104579
1 parent 667e4ec commit 7b3bc95

File tree

4 files changed

+89
-4
lines changed

4 files changed

+89
-4
lines changed

Lib/test/test_sys_settrace.py

+56
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,62 @@ def func():
15711571

15721572
self.run_and_compare(func, EXPECTED_EVENTS)
15731573

1574+
def test_settrace_error(self):
1575+
1576+
raised = False
1577+
def error_once(frame, event, arg):
1578+
nonlocal raised
1579+
if not raised:
1580+
raised = True
1581+
raise Exception
1582+
return error
1583+
1584+
try:
1585+
sys._getframe().f_trace = error_once
1586+
sys.settrace(error_once)
1587+
len([])
1588+
except Exception as ex:
1589+
count = 0
1590+
tb = ex.__traceback__
1591+
print(tb)
1592+
while tb:
1593+
if tb.tb_frame.f_code.co_name == "test_settrace_error":
1594+
count += 1
1595+
tb = tb.tb_next
1596+
if count == 0:
1597+
self.fail("Traceback is missing frame")
1598+
elif count > 1:
1599+
self.fail("Traceback has frame more than once")
1600+
else:
1601+
self.fail("No exception raised")
1602+
finally:
1603+
sys.settrace(None)
1604+
1605+
@support.cpython_only
1606+
def test_testcapi_settrace_error(self):
1607+
1608+
# Skip this test if the _testcapi module isn't available.
1609+
_testcapi = import_helper.import_module('_testcapi')
1610+
1611+
try:
1612+
_testcapi.settrace_to_error([])
1613+
len([])
1614+
except Exception as ex:
1615+
count = 0
1616+
tb = ex.__traceback__
1617+
while tb:
1618+
if tb.tb_frame.f_code.co_name == "test_testcapi_settrace_error":
1619+
count += 1
1620+
tb = tb.tb_next
1621+
if count == 0:
1622+
self.fail("Traceback is missing frame")
1623+
elif count > 1:
1624+
self.fail("Traceback has frame more than once")
1625+
else:
1626+
self.fail("No exception raised")
1627+
finally:
1628+
sys.settrace(None)
1629+
15741630
def test_very_large_function(self):
15751631
# There is a separate code path when the number of lines > (1 << 15).
15761632
d = {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Do not add a frame to the traceback in the ``sys.setprofile`` and
2+
``sys.settrace`` trampoline functions. This ensures that frames are not
3+
duplicated if an exception is raised in the callback function, and ensures
4+
that frames are not omitted if a C callback is used and that does not add
5+
the frame.

Modules/_testcapimodule.c

+28
Original file line numberDiff line numberDiff line change
@@ -6345,6 +6345,33 @@ settrace_to_record(PyObject *self, PyObject *list)
63456345
Py_RETURN_NONE;
63466346
}
63476347

6348+
static int
6349+
error_func(PyObject *obj, PyFrameObject *f, int what, PyObject *arg)
6350+
{
6351+
assert(PyList_Check(obj));
6352+
/* Only raise if list is empty, otherwise append None
6353+
* This ensures that we only raise once */
6354+
if (PyList_GET_SIZE(obj)) {
6355+
return 0;
6356+
}
6357+
if (PyList_Append(obj, Py_None)) {
6358+
return -1;
6359+
}
6360+
PyErr_SetString(PyExc_Exception, "an exception");
6361+
return -1;
6362+
}
6363+
6364+
static PyObject *
6365+
settrace_to_error(PyObject *self, PyObject *list)
6366+
{
6367+
if (!PyList_Check(list)) {
6368+
PyErr_SetString(PyExc_TypeError, "argument must be a list");
6369+
return NULL;
6370+
}
6371+
PyEval_SetTrace(error_func, list);
6372+
Py_RETURN_NONE;
6373+
}
6374+
63486375
static PyObject *negative_dictoffset(PyObject *, PyObject *);
63496376

63506377
static PyObject *
@@ -6694,6 +6721,7 @@ static PyMethodDef TestMethods[] = {
66946721
{"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
66956722
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
66966723
{"test_code_api", test_code_api, METH_NOARGS, NULL},
6724+
{"settrace_to_error", settrace_to_error, METH_O, NULL},
66976725
{"settrace_to_record", settrace_to_record, METH_O, NULL},
66986726
{"function_get_code", function_get_code, METH_O, NULL},
66996727
{"function_get_globals", function_get_globals, METH_O, NULL},

Python/sysmodule.c

-4
Original file line numberDiff line numberDiff line change
@@ -950,10 +950,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback,
950950
PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3);
951951

952952
PyFrame_LocalsToFast(frame, 1);
953-
if (result == NULL) {
954-
PyTraceBack_Here(frame);
955-
}
956-
957953
return result;
958954
}
959955

0 commit comments

Comments
 (0)