从C API访问Python回溯
我在找出使用C API走Python追踪的正确方法时遇到了一些困难。 我正在编写嵌入Python解释器的应用程序。 我希望能够执行任意Python代码,并且如果它引发异常,请将其转换为我自己的特定于应用程序的C ++异常。 目前,仅提取Python异常引发的文件名和行号就足够了。 这是我到目前为止:
PyObject* pyresult = PyObject_CallObject(someCallablePythonObject, someArgs);
if (!pyresult)
{
PyObject* excType, *excValue, *excTraceback;
PyErr_Fetch(&excType, &excValue, &excTraceback);
PyErr_NormalizeException(&excType, &excValue, &excTraceback);
PyTracebackObject* traceback = (PyTracebackObject*)traceback;
// Advance to the last frame (python puts the most-recent call at the end)
while (traceback->tb_next != NULL)
traceback = traceback->tb_next;
// At this point I have access to the line number via traceback->tb_lineno,
// but where do I get the file name from?
// ...
}
在Python源代码中挖掘,我看到他们通过_frame
结构访问当前帧的文件名和模块名称,这看起来像是一个私人定义的结构。 我的下一个想法是以编程方式加载Python的'traceback'模块并用C API调用它的函数。 这是否理智? 有没有更好的方法来从C访问Python追溯?
我更喜欢从C调用python:
err = PyErr_Occurred();
if (err != NULL) {
PyObject *ptype, *pvalue, *ptraceback;
PyObject *pystr, *module_name, *pyth_module, *pyth_func;
char *str;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
pystr = PyObject_Str(pvalue);
str = PyString_AsString(pystr);
error_description = strdup(str);
/* See if we can get a full traceback */
module_name = PyString_FromString("traceback");
pyth_module = PyImport_Import(module_name);
Py_DECREF(module_name);
if (pyth_module == NULL) {
full_backtrace = NULL;
return;
}
pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
if (pyth_func && PyCallable_Check(pyth_func)) {
PyObject *pyth_val;
pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
pystr = PyObject_Str(pyth_val);
str = PyString_AsString(pystr);
full_backtrace = strdup(str);
Py_DECREF(pyth_val);
}
}
这是一个古老的问题,但为了将来的参考,您可以从线程状态对象中获取当前的堆栈帧,然后向后走帧。 除非您想保留未来的状态,否则回溯对象不是必需的。
例如:
PyThreadState *tstate = PyThreadState_GET();
if (NULL != tstate && NULL != tstate->frame) {
PyFrameObject *frame = tstate->frame;
printf("Python stack trace:n");
while (NULL != frame) {
// int line = frame->f_lineno;
/*
frame->f_lineno will not always return the correct line number
you need to call PyCode_Addr2Line().
*/
int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti);
const char *filename = PyString_AsString(frame->f_code->co_filename);
const char *funcname = PyString_AsString(frame->f_code->co_name);
printf(" %s(%d): %sn", filename, line, funcname);
frame = frame->f_back;
}
}
我发现_frame
实际上是在Python包含的frameobject.h
头文件中定义的。 在此基础上加上Python C实现中的traceback.c
,我们得到:
#include <Python.h>
#include <frameobject.h>
PyTracebackObject* traceback = get_the_traceback();
int line = traceback->tb_lineno;
const char* filename = PyString_AsString(traceback->tb_frame->f_code->co_filename);
但是这对我来说似乎仍然很肮脏。
链接地址: http://www.djcxy.com/p/55183.html