Skip to content

Commit c26d03d

Browse files
authored
GH-102818: Do not call PyTraceBack_Here in sys.settrace trampoline. (GH-104579)
1 parent 616fcad commit c26d03d

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
@@ -1568,6 +1568,62 @@ def func():
15681568

15691569
self.run_and_compare(func, EXPECTED_EVENTS)
15701570

1571+
def test_settrace_error(self):
1572+
1573+
raised = False
1574+
def error_once(frame, event, arg):
1575+
nonlocal raised
1576+
if not raised:
1577+
raised = True
1578+
raise Exception
1579+
return error
1580+
1581+
try:
1582+
sys._getframe().f_trace = error_once
1583+
sys.settrace(error_once)
1584+
len([])
1585+
except Exception as ex:
1586+
count = 0
1587+
tb = ex.__traceback__
1588+
print(tb)
1589+
while tb:
1590+
if tb.tb_frame.f_code.co_name == "test_settrace_error":
1591+
count += 1
1592+
tb = tb.tb_next
1593+
if count == 0:
1594+
self.fail("Traceback is missing frame")
1595+
elif count > 1:
1596+
self.fail("Traceback has frame more than once")
1597+
else:
1598+
self.fail("No exception raised")
1599+
finally:
1600+
sys.settrace(None)
1601+
1602+
@support.cpython_only
1603+
def test_testcapi_settrace_error(self):
1604+
1605+
# Skip this test if the _testcapi module isn't available.
1606+
_testcapi = import_helper.import_module('_testcapi')
1607+
1608+
try:
1609+
_testcapi.settrace_to_error([])
1610+
len([])
1611+
except Exception as ex:
1612+
count = 0
1613+
tb = ex.__traceback__
1614+
while tb:
1615+
if tb.tb_frame.f_code.co_name == "test_testcapi_settrace_error":
1616+
count += 1
1617+
tb = tb.tb_next
1618+
if count == 0:
1619+
self.fail("Traceback is missing frame")
1620+
elif count > 1:
1621+
self.fail("Traceback has frame more than once")
1622+
else:
1623+
self.fail("No exception raised")
1624+
finally:
1625+
sys.settrace(None)
1626+
15711627
def test_very_large_function(self):
15721628
# There is a separate code path when the number of lines > (1 << 15).
15731629
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 the
5+
frame.

Modules/_testcapimodule.c

+28
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,33 @@ settrace_to_record(PyObject *self, PyObject *list)
30623062
Py_RETURN_NONE;
30633063
}
30643064

3065+
static int
3066+
error_func(PyObject *obj, PyFrameObject *f, int what, PyObject *arg)
3067+
{
3068+
assert(PyList_Check(obj));
3069+
/* Only raise if list is empty, otherwise append None
3070+
* This ensures that we only raise once */
3071+
if (PyList_GET_SIZE(obj)) {
3072+
return 0;
3073+
}
3074+
if (PyList_Append(obj, Py_None)) {
3075+
return -1;
3076+
}
3077+
PyErr_SetString(PyExc_Exception, "an exception");
3078+
return -1;
3079+
}
3080+
3081+
static PyObject *
3082+
settrace_to_error(PyObject *self, PyObject *list)
3083+
{
3084+
if (!PyList_Check(list)) {
3085+
PyErr_SetString(PyExc_TypeError, "argument must be a list");
3086+
return NULL;
3087+
}
3088+
PyEval_SetTrace(error_func, list);
3089+
Py_RETURN_NONE;
3090+
}
3091+
30653092
static PyObject *
30663093
clear_managed_dict(PyObject *self, PyObject *obj)
30673094
{
@@ -3352,6 +3379,7 @@ static PyMethodDef TestMethods[] = {
33523379
{"gen_get_code", gen_get_code, METH_O, NULL},
33533380
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
33543381
{"test_code_api", test_code_api, METH_NOARGS, NULL},
3382+
{"settrace_to_error", settrace_to_error, METH_O, NULL},
33553383
{"settrace_to_record", settrace_to_record, METH_O, NULL},
33563384
{"test_macros", test_macros, METH_NOARGS, NULL},
33573385
{"clear_managed_dict", clear_managed_dict, 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)