sys.settrace分析环境
本文环境python3.5.2
sys.settrace函数执行
首先我们继续查看示例代码如下:
import sys
def trace(frame, event, arg_unused):
print(frame.f_lineno, event, arg_unused)
return trace
sys.settrace(trace)
with open('test_file.py', "rb") as f:
source = f.read()
code = compile(source, 'test_file.py', 'exec')
exec(code)
继续使用上文分析过的脚本test_file.py文件,test_file.py文件如下:
import sys
import os
def test_a():
a = 1
def test_b():
b = 2
def run():
if 1:
r = 1
else:
r = 2
test_a()
ret = r
run()
此时运行脚本输出的内容如下:
1 call None
1 line None
3 line None
6 line None
10 line None
14 line None
23 line None
14 call None
16 line None
19 line None
6 call None
7 line None
7 return None
20 line None
20 return None
23 return None
此时,我们继续修改示例代码,将trace函数中注释掉return trace这一行,此时继续运行,输出结果如下:
1 call None
14 call None
6 call None
此时的输出结果如上所述,为什么此时两次运行的结果会出现差异呢,本文我们就分析sys.settrace的整个执行流程。
sys.settrace函数的源码流程
首先,在Python3.5.2的源码中找到sysmodule.c文件,该文件中就是c实现的sys模块的相关函数;
static PyMethodDef sys_methods[] = {
...
{"settrace", sys_settrace, METH_O, settrace_doc},
{"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc},
...
{NULL, NULL} /* sentinel */
};
此时调用的sys.settrace函数就是调用了sys_settrace函数;
static PyObject *
sys_settrace(PyObject *self, PyObject *args)
{
if (trace_init() == -1) // 初始化trace
return NULL;
if (args == Py_None)
PyEval_SetTrace(NULL, NULL); // 如果初始化传入的函数为None则设置空
else
PyEval_SetTrace(trace_trampoline, args); // 否则设置该函数
Py_INCREF(Py_None);
return Py_None; // 返回None
}
static int
trace_init(void)
{
static char *whatnames[7] = {"call", "exception", "line", "return",
"c_call", "c_exception", "c_return"}; // 定义了7个事件对应的描述字符
PyObject *name;
int i;
for (i = 0; i < 7; ++i) {
if (whatstrings[i] == NULL) {
name = PyUnicode_InternFromString(whatnames[i]); // 遍历转换为python字符串
if (name == NULL)
return -1;
whatstrings[i] = name; // 设置对应的Python字符串
}
}
return 0;
}
#define PyTrace_CALL 0
#define PyTrace_EXCEPTION 1
#define PyTrace_LINE 2
#define PyTrace_RETURN 3
#define PyT