不幸的是,正如评论中所讨论的,这并非在所有情况下都是可能的。创建上下文管理器时,将运行以下代码(至少在 cPython 2.7 中。我无法评论其他实现):
case SETUP_WITH:
{
static PyObject *exit, *enter;
w = TOP();
x = special_lookup(w, "__exit__", &exit);
if (!x)
break;
SET_TOP(x);
/* more code follows... */
}
该__exit__
方法使用宏被压入堆栈,SET_TOP
宏定义为:
#define SET_TOP(v) (stack_pointer[-1] = (v))
反过来,堆栈指针在帧 eval开始时设置到帧的值堆栈的顶部:
stack_pointer = f->f_stacktop;
其中 f 是在frameobject.h中定义的框架对象。对我们来说不幸的是,这是小径停止的地方。python 可访问框架对象仅使用以下方法定义:
static PyMemberDef frame_memberlist[] = {
{"f_back", T_OBJECT, OFF(f_back), RO},
{"f_code", T_OBJECT, OFF(f_code), RO},
{"f_builtins", T_OBJECT, OFF(f_builtins),RO},
{"f_globals", T_OBJECT, OFF(f_globals), RO},
{"f_lasti", T_INT, OFF(f_lasti), RO},
{NULL} /* Sentinel */
};
不幸的是,其中不包括f_valuestack
我们需要的。这是有道理的,因为f_valuestack
它是 type PyObject **
,它需要被包装在一个对象中,才能以任何方式从 python 访问。
TL;DR:__exit__
我们正在寻找的方法只位于一个地方,即框架对象的值堆栈,而 cPython 不会使值堆栈可供 python 代码访问。