diff -r e539761d45e3 -r a1bb43bbf797 Include/frameobject.h --- a/Include/frameobject.h Thu Nov 14 15:35:47 2013 +0100 +++ b/Include/frameobject.h Thu Nov 14 18:02:26 2013 +0100 @@ -41,6 +41,7 @@ typedef struct _frame { PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ + PyObject *f_annotation; /* shown in traceback */ /* Call PyFrame_GetLineNumber() instead of reading this field directly. As of 2.3 f_lineno is only valid when tracing is active (i.e. when f_trace is set). At other times we use diff -r e539761d45e3 -r a1bb43bbf797 Lib/traceback.py --- a/Lib/traceback.py Thu Nov 14 15:35:47 2013 +0100 +++ b/Lib/traceback.py Thu Nov 14 18:02:26 2013 +0100 @@ -15,8 +15,11 @@ import operator # def _format_list_iter(extracted_list): - for filename, lineno, name, line in extracted_list: - item = ' File "{}", line {}, in {}\n'.format(filename, lineno, name) + for filename, lineno, name, line, annotation in extracted_list: + item = ' File "{}", line {}, in {}'.format(filename, lineno, name) + if annotation is not None: + item += ': {}'.format(annotation) + item += '\n' if line: item = item + ' {}\n'.format(line.strip()) yield item @@ -60,6 +63,7 @@ def _extract_tb_or_stack_iter(curr, limi co = f.f_code filename = co.co_filename name = co.co_name + annotation = f.f_annotation linecache.checkcache(filename) line = linecache.getline(filename, lineno, f.f_globals) @@ -69,7 +73,7 @@ def _extract_tb_or_stack_iter(curr, limi else: line = None - yield (filename, lineno, name, line) + yield (filename, lineno, name, line, annotation) curr = next_item n += 1 @@ -310,3 +314,16 @@ def clear_frames(tb): # Ignore the exception raised if the frame is still executing. pass tb = tb.tb_next + +def annotate(format): + def wrapper(f): + def wrapped(*args, **kwargs): + try: + f(*args, **kwargs) + except Exception as exc: + frame = exc.__traceback__.tb_next.tb_frame + annotation = format.format(**frame.f_locals) + frame.f_annotation = annotation + raise + return wrapped + return wrapper diff -r e539761d45e3 -r a1bb43bbf797 Objects/frameobject.c --- a/Objects/frameobject.c Thu Nov 14 15:35:47 2013 +0100 +++ b/Objects/frameobject.c Thu Nov 14 18:02:26 2013 +0100 @@ -15,6 +15,7 @@ static PyMemberDef frame_memberlist[] = {"f_builtins", T_OBJECT, OFF(f_builtins), READONLY}, {"f_globals", T_OBJECT, OFF(f_globals), READONLY}, {"f_lasti", T_INT, OFF(f_lasti), READONLY}, + {"f_annotation", T_OBJECT, OFF(f_annotation), 0}, {NULL} /* Sentinel */ }; @@ -442,6 +443,7 @@ frame_dealloc(PyFrameObject *f) Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); + Py_CLEAR(f->f_annotation); co = f->f_code; if (co->co_zombieframe == NULL) @@ -473,6 +475,7 @@ frame_traverse(PyFrameObject *f, visitpr Py_VISIT(f->f_exc_type); Py_VISIT(f->f_exc_value); Py_VISIT(f->f_exc_traceback); + Py_VISIT(f->f_annotation); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -506,6 +509,7 @@ frame_tp_clear(PyFrameObject *f) Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); + Py_CLEAR(f->f_annotation); Py_CLEAR(f->f_trace); /* locals */ @@ -730,6 +734,7 @@ PyFrame_New(PyThreadState *tstate, PyCod f->f_lasti = -1; f->f_lineno = code->co_firstlineno; + f->f_annotation = NULL; f->f_iblock = 0; f->f_executing = 0; f->f_gen = NULL; diff -r e539761d45e3 -r a1bb43bbf797 Python/traceback.c --- a/Python/traceback.c Thu Nov 14 15:35:47 2013 +0100 +++ b/Python/traceback.c Thu Nov 14 18:02:26 2013 +0100 @@ -343,15 +343,20 @@ int } static int -tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name) +tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name, + PyObject *annotation) { int err; PyObject *line; if (filename == NULL || name == NULL) return -1; - line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U\n", - filename, lineno, name); + if (annotation == NULL) + line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U\n", + filename, lineno, name); + else + line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U: %U\n", + filename, lineno, name, annotation); if (line == NULL) return -1; err = PyFile_WriteObject(line, f, Py_PRINT_RAW); @@ -379,7 +384,8 @@ tb_printinternal(PyTracebackObject *tb, err = tb_displayline(f, tb->tb_frame->f_code->co_filename, tb->tb_lineno, - tb->tb_frame->f_code->co_name); + tb->tb_frame->f_code->co_name, + tb->tb_frame->f_annotation); } depth--; tb = tb->tb_next;