Adding extra frames to traceback in C module

R

Roger Binns

One thing I would like to do in my extension module is
add extra frames to the traceback when an extension
occurs. At the moment C code is invisible to tracebacks.
This is relevant when the C code makes a Python callback.
For example if the following code sequence happens
(time going down)

Python C

A
B
C
D
E
F
G
H

If there is an exception in H then the traceback seen
from A will be "B C G H". I want to get D, E and F
into there as well. This is because "G H" could be
called for several different reasons in the C code
and adding the extra information will establish why.

I couldn't find anything on the web or in the documentation
to explain how to do it. I did find snippets of code
doing things like PyTraceback_Here but they use a real
Python frame which I don't have and don't know how to
synthesize.

Roger
 
J

John Machin

One thing I would like to do in my extension module is
add extra frames to the traceback when an extension
occurs. At the moment C code is invisible to tracebacks.
This is relevant when the C code makes a Python callback.
[snip]

I couldn't find anything on the web or in the documentation
to explain how to do it. I did find snippets of code
doing things like PyTraceback_Here but they use a real
Python frame which I don't have and don't know how to
synthesize.

In the C code generated by Pyrex, the frame is faked up on the fly if an
error occurs:

C:\junk>demotrbackmain.py
Traceback (most recent call last):
File "C:\junk\demotrbackmain.py", line 12, in ?
funcb()
File "C:\junk\demotrbackmain.py", line 3, in funcb
demotrback.funcc(funce)
File "demotrback.pyx", line 2, in demotrback.funcc
funcd1(cbfunc)
File "demotrback.pyx", line 5, in demotrback.funcd1
funcd2(callb)
File "demotrback.pyx", line 8, in demotrback.funcd2
callb()
File "C:\junk\demotrbackmain.py", line 6, in funce
funcf()
File "C:\junk\demotrbackmain.py", line 9, in funcf
x = 1 / 0
ZeroDivisionError: integer division or modulo by zero

Check out
http://www.cosc.canterbury.ac.nz/~greg/python/Pyrex/version/Doc/overview.html#ExceptionValues
then whip up some quick examples and look at the generated code.

HTH,
John
 
G

greg

Roger said:
One thing I would like to do in my extension module is
add extra frames to the traceback when an extension
occurs.

I did find snippets of code
doing things like PyTraceback_Here but they use a real
Python frame which I don't have and don't know how to
synthesize.

This is the code Pyrex uses to add frames to the traceback.
It creates a fake frame object and fills in just enough
of it to keep the traceback printing code happy.

Or you could just use Pyrex for your extension module
in the first place, and get it done for you automatically.

-----------------------------------------------------------
#include "compile.h"
#include "frameobject.h"
#include "traceback.h"

static void __Pyx_AddTraceback(char *funcname) {
PyObject *py_srcfile = 0;
PyObject *py_funcname = 0;
PyObject *py_globals = 0;
PyObject *empty_tuple = 0;
PyObject *empty_string = 0;
PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0;

py_srcfile = PyString_FromString(%(FILENAME)s);
if (!py_srcfile) goto bad;
py_funcname = PyString_FromString(funcname);
if (!py_funcname) goto bad;
py_globals = PyModule_GetDict(%(GLOBALS)s);
if (!py_globals) goto bad;
empty_tuple = PyTuple_New(0);
if (!empty_tuple) goto bad;
empty_string = PyString_FromString("");
if (!empty_string) goto bad;
py_code = PyCode_New(
0, /*int argcount,*/
0, /*int nlocals,*/
0, /*int stacksize,*/
0, /*int flags,*/
empty_string, /*PyObject *code,*/
empty_tuple, /*PyObject *consts,*/
empty_tuple, /*PyObject *names,*/
empty_tuple, /*PyObject *varnames,*/
empty_tuple, /*PyObject *freevars,*/
empty_tuple, /*PyObject *cellvars,*/
py_srcfile, /*PyObject *filename,*/
py_funcname, /*PyObject *name,*/
%(LINENO)s, /*int firstlineno,*/
empty_string /*PyObject *lnotab*/
);
if (!py_code) goto bad;
py_frame = PyFrame_New(
PyThreadState_Get(), /*PyThreadState *tstate,*/
py_code, /*PyCodeObject *code,*/
py_globals, /*PyObject *globals,*/
0 /*PyObject *locals*/
);
if (!py_frame) goto bad;
py_frame->f_lineno = %(LINENO)s;
PyTraceBack_Here(py_frame);
bad:
Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname);
Py_XDECREF(empty_tuple);
Py_XDECREF(empty_string);
Py_XDECREF(py_code);
Py_XDECREF(py_frame);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top