view Python/traceback.c @ 45774:058fbfb985ae

Merged revisions 61404-61407 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r61404 | raymond.hettinger | 2008-03-15 21:02:04 +0100 (Sat, 15 Mar 2008) | 17 lines Removed Exact/Inexact after discussion with Yasskin. Unlike Scheme where exactness is implemented as taints, the Python implementation associated exactness with data types. This created inheritance issues (making an exact subclass of floats would result in the subclass having both an explicit Exact registration and an inherited Inexact registration). This was a problem for the decimal module which was designed to span both exact and inexact arithmetic. There was also a question of use cases and no examples were found where ABCs for exactness could be used to improve code. One other issue was having separate tags for both the affirmative and negative cases. This is at odds with the approach taken elsewhere in the Python (i.e. we don't have an ABC both Hashable and Unhashable). ........ r61405 | raymond.hettinger | 2008-03-15 21:37:50 +0100 (Sat, 15 Mar 2008) | 1 line Zap one more use of Exact/Inexact. ........ r61406 | neal.norwitz | 2008-03-15 23:03:18 +0100 (Sat, 15 Mar 2008) | 9 lines Add a warning for code like: assert (0, 'message') An empty tuple does not create a warning. While questionable usage: assert (), 'message' should not display a warning. Tested manually. The warning message could be improved. Feel free to update it. ........ r61407 | neal.norwitz | 2008-03-15 23:36:01 +0100 (Sat, 15 Mar 2008) | 1 line Handle memory allocation failure. Found by Adam Olsen ........
author Christian Heimes <christian@cheimes.de>
date Sun, 16 Mar 2008 00:32:36 +0000
parents 14d4104976fc
children 47d9bbe10897
line wrap: on
line source


/* Traceback implementation */

#include "Python.h"

#include "code.h"
#include "frameobject.h"
#include "structmember.h"
#include "osdefs.h"
#include "traceback.h"

#define OFF(x) offsetof(PyTracebackObject, x)

static PyObject *
tb_dir(PyTracebackObject *self)
{
    return Py_BuildValue("[ssss]", "tb_frame", "tb_next",
                                   "tb_lasti", "tb_lineno");
}

static PyMethodDef tb_methods[] = {
   {"__dir__", (PyCFunction)tb_dir, METH_NOARGS},
   {NULL, NULL, 0, NULL},
};

static PyMemberDef tb_memberlist[] = {
	{"tb_next",	T_OBJECT,	OFF(tb_next),	READONLY},
	{"tb_frame",	T_OBJECT,	OFF(tb_frame),	READONLY},
	{"tb_lasti",	T_INT,		OFF(tb_lasti),	READONLY},
	{"tb_lineno",	T_INT,		OFF(tb_lineno),	READONLY},
	{NULL}	/* Sentinel */
};

static void
tb_dealloc(PyTracebackObject *tb)
{
	PyObject_GC_UnTrack(tb);
	Py_TRASHCAN_SAFE_BEGIN(tb)
	Py_XDECREF(tb->tb_next);
	Py_XDECREF(tb->tb_frame);
	PyObject_GC_Del(tb);
	Py_TRASHCAN_SAFE_END(tb)
}

static int
tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
{
	Py_VISIT(tb->tb_next);
	Py_VISIT(tb->tb_frame);
	return 0;
}

static void
tb_clear(PyTracebackObject *tb)
{
	Py_CLEAR(tb->tb_next);
	Py_CLEAR(tb->tb_frame);
}

PyTypeObject PyTraceBack_Type = {
	PyVarObject_HEAD_INIT(&PyType_Type, 0)
	"traceback",
	sizeof(PyTracebackObject),
	0,
	(destructor)tb_dealloc, /*tp_dealloc*/
	0,		/*tp_print*/
	0,    /*tp_getattr*/
	0,		/*tp_setattr*/
	0,		/*tp_compare*/
	0,		/*tp_repr*/
	0,		/*tp_as_number*/
	0,		/*tp_as_sequence*/
	0,		/*tp_as_mapping*/
	0,		/* tp_hash */
	0,		/* tp_call */
	0,		/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,		/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
	0,             				/* tp_doc */
 	(traverseproc)tb_traverse,		/* tp_traverse */
	(inquiry)tb_clear,			/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	tb_methods,	/* tp_methods */
	tb_memberlist,	/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
};

static PyTracebackObject *
newtracebackobject(PyTracebackObject *next, PyFrameObject *frame)
{
	PyTracebackObject *tb;
	if ((next != NULL && !PyTraceBack_Check(next)) ||
			frame == NULL || !PyFrame_Check(frame)) {
		PyErr_BadInternalCall();
		return NULL;
	}
	tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
	if (tb != NULL) {
		Py_XINCREF(next);
		tb->tb_next = next;
		Py_XINCREF(frame);
		tb->tb_frame = frame;
		tb->tb_lasti = frame->f_lasti;
		tb->tb_lineno = PyCode_Addr2Line(frame->f_code, 
						 frame->f_lasti);
		PyObject_GC_Track(tb);
	}
	return tb;
}

int
PyTraceBack_Here(PyFrameObject *frame)
{
	PyThreadState *tstate = PyThreadState_GET();
	PyTracebackObject *oldtb = (PyTracebackObject *) tstate->curexc_traceback;
	PyTracebackObject *tb = newtracebackobject(oldtb, frame);
	if (tb == NULL)
		return -1;
	tstate->curexc_traceback = (PyObject *)tb;
	Py_XDECREF(oldtb);
	return 0;
}

static int
tb_displayline(PyObject *f, char *filename, int lineno, char *name)
{
	int err = 0;
	FILE *xfp;
	char linebuf[2000];
	int i;
	char namebuf[MAXPATHLEN+1];

	if (filename == NULL || name == NULL)
		return -1;
	/* This is needed by Emacs' compile command */
#define FMT "  File \"%.500s\", line %d, in %.500s\n"
	xfp = fopen(filename, "r" PY_STDIOTEXTMODE);
	if (xfp == NULL) {
		/* Search tail of filename in sys.path before giving up */
		PyObject *path;
		char *tail = strrchr(filename, SEP);
		if (tail == NULL)
			tail = filename;
		else
			tail++;
		path = PySys_GetObject("path");
		if (path != NULL && PyList_Check(path)) {
			Py_ssize_t _npath = PyList_Size(path);
			int npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int);
			size_t taillen = strlen(tail);
			for (i = 0; i < npath; i++) {
				PyObject *v = PyList_GetItem(path, i);
				if (v == NULL) {
					PyErr_Clear();
					break;
				}
				if (PyString_Check(v)) {
					size_t len;
					len = PyString_GET_SIZE(v);
					if (len + 1 + taillen >= MAXPATHLEN)
						continue; /* Too long */
					strcpy(namebuf, PyString_AsString(v));
					if (strlen(namebuf) != len)
						continue; /* v contains '\0' */
					if (len > 0 && namebuf[len-1] != SEP)
						namebuf[len++] = SEP;
					strcpy(namebuf+len, tail);
					xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE);
					if (xfp != NULL) {
						filename = namebuf;
						break;
					}
				}
			}
		}
	}
	PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
	err = PyFile_WriteString(linebuf, f);
	if (xfp == NULL)
		return err;
	else if (err != 0) {
		fclose(xfp);
		return err;
	}
	for (i = 0; i < lineno; i++) {
		char* pLastChar = &linebuf[sizeof(linebuf)-2];
		do {
			*pLastChar = '\0';
			if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL)
				break;
			/* fgets read *something*; if it didn't get as
			   far as pLastChar, it must have found a newline
			   or hit the end of the file;	if pLastChar is \n,
			   it obviously found a newline; else we haven't
			   yet seen a newline, so must continue */
		} while (*pLastChar != '\0' && *pLastChar != '\n');
	}
	if (i == lineno) {
		char *p = linebuf;
		while (*p == ' ' || *p == '\t' || *p == '\014')
			p++;
		err = PyFile_WriteString("    ", f);
		if (err == 0) {
			err = PyFile_WriteString(p, f);
			if (err == 0 && strchr(p, '\n') == NULL)
				err = PyFile_WriteString("\n", f);
		}
	}
	fclose(xfp);
	return err;
}

static int
tb_printinternal(PyTracebackObject *tb, PyObject *f, int limit)
{
	int err = 0;
	int depth = 0;
	PyTracebackObject *tb1 = tb;
	while (tb1 != NULL) {
		depth++;
		tb1 = tb1->tb_next;
	}
	while (tb != NULL && err == 0) {
		if (depth <= limit) {
			err = tb_displayline(f,
			    PyUnicode_AsString(
				    tb->tb_frame->f_code->co_filename),
			    tb->tb_lineno,
			    PyUnicode_AsString(tb->tb_frame->f_code->co_name));
		}
		depth--;
		tb = tb->tb_next;
		if (err == 0)
			err = PyErr_CheckSignals();
	}
	return err;
}

#define PyTraceBack_LIMIT 1000

int
PyTraceBack_Print(PyObject *v, PyObject *f)
{
	int err;
	PyObject *limitv;
	int limit = PyTraceBack_LIMIT;

	if (v == NULL)
		return 0;
	if (!PyTraceBack_Check(v)) {
		PyErr_BadInternalCall();
		return -1;
	}
	limitv = PySys_GetObject("tracebacklimit");
	if (limitv) {
		PyObject *exc_type, *exc_value, *exc_tb;

		PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
		limit = PyLong_AsLong(limitv);
		if (limit == -1 && PyErr_Occurred()) {
			if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
				limit = PyTraceBack_LIMIT;
			}
			else {
				Py_XDECREF(exc_type);
				Py_XDECREF(exc_value);
				Py_XDECREF(exc_tb);
				return 0;
			}
		}
		else if (limit <= 0) {
			limit = PyTraceBack_LIMIT;
		}
		PyErr_Restore(exc_type, exc_value, exc_tb);
	}
	err = PyFile_WriteString("Traceback (most recent call last):\n", f);
	if (!err)
		err = tb_printinternal((PyTracebackObject *)v, f, limit);
	return err;
}