view Python/traceback.c @ 52528:2b436310fc8b

Merged revisions 70801,70809 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk The merge ran into a lot of conflicts because dicts were replaced with sets in the Python 3 version of the symbol table. ........ r70801 | jeremy.hylton | 2009-03-31 09:17:03 -0400 (Tue, 31 Mar 2009) | 3 lines Add is_declared_global() which distinguishes between implicit and explicit global variables. ........ r70809 | jeremy.hylton | 2009-03-31 09:48:15 -0400 (Tue, 31 Mar 2009) | 14 lines Global statements from one function leaked into parallel functions. Re http://bugs.python.org/issue4315 The symbol table used the same name dictionaries to recursively analyze each of its child blocks, even though the dictionaries are modified during analysis. The fix is to create new temporary dictionaries via the analyze_child_block(). The only information that needs to propagate back up is the names of the free variables. Add more comments and break out a helper function. This code doesn't get any easier to understand when you only look at it once a year. ........
author Jeremy Hylton <jeremy@alum.mit.edu>
date Tue, 31 Mar 2009 15:26:37 +0000
parents 87fbbf22adf3
children f197407bdba2 79718ee03986
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"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

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

/* Method from Parser/tokenizer.c */
extern char * PyTokenizer_FindEncoding(int);

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_reserved*/
	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
_Py_FindSourceFile(const char* filename, char* namebuf, size_t namelen, int open_flags)
{
	int i;
	int fd = -1;
	PyObject *v;
	Py_ssize_t _npath;
	int npath;
	size_t taillen;
	PyObject *syspath;
	const char* path;
	const char* tail;
	Py_ssize_t len;

	/* Search tail of filename in sys.path before giving up */
	tail = strrchr(filename, SEP);
	if (tail == NULL)
		tail = filename;
	else
		tail++;
	taillen = strlen(tail);

	syspath = PySys_GetObject("path");
	if (syspath == NULL || !PyList_Check(syspath))
		return -1;
	_npath = PyList_Size(syspath);
	npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int);

	for (i = 0; i < npath; i++) {
		v = PyList_GetItem(syspath, i);
		if (v == NULL) {
			PyErr_Clear();
			break;
		}
		if (!PyUnicode_Check(v))
			continue;
		path = _PyUnicode_AsStringAndSize(v, &len);
		if (len + 1 + (Py_ssize_t)taillen >= (Py_ssize_t)namelen - 1)
			continue; /* Too long */
		strcpy(namebuf, path);
		if (strlen(namebuf) != len)
			continue; /* v contains '\0' */
		if (len > 0 && namebuf[len-1] != SEP)
			namebuf[len++] = SEP;
		strcpy(namebuf+len, tail);
		Py_BEGIN_ALLOW_THREADS
		fd = open(namebuf, open_flags);
		Py_END_ALLOW_THREADS
		if (0 <= fd) {
			return fd;
		}
	}
	return -1;
}

int
_Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno, int indent)
{
	int err = 0;
	int fd;
	int i;
	char *found_encoding;
	char *encoding;
	PyObject *fob = NULL;
	PyObject *lineobj = NULL;
#ifdef O_BINARY
	const int open_flags = O_RDONLY | O_BINARY;   /* necessary for Windows */
#else
	const int open_flags = O_RDONLY;
#endif
	char buf[MAXPATHLEN+1];
	Py_UNICODE *u, *p;
	Py_ssize_t len;

	/* open the file */
	if (filename == NULL)
		return 0;
	Py_BEGIN_ALLOW_THREADS
	fd = open(filename, open_flags);
	Py_END_ALLOW_THREADS
	if (fd < 0) {
		fd = _Py_FindSourceFile(filename, buf, sizeof(buf), open_flags);
		if (fd < 0)
			return 0;
		filename = buf;
	}

	/* use the right encoding to decode the file as unicode */
	found_encoding = PyTokenizer_FindEncoding(fd);
	encoding = (found_encoding != NULL) ? found_encoding :
		(char*)PyUnicode_GetDefaultEncoding();
	lseek(fd, 0, 0); /* Reset position */
	fob = PyFile_FromFd(fd, (char*)filename, "r", -1, (char*)encoding,
		NULL, NULL, 1);
	PyMem_FREE(found_encoding);
	if (fob == NULL) {
		PyErr_Clear();
		close(fd);
		return 0;
	}

	/* get the line number lineno */
	for (i = 0; i < lineno; i++) {
		Py_XDECREF(lineobj);
		lineobj = PyFile_GetLine(fob, -1);
		if (!lineobj) {
			err = -1;
			break;
		}
	}
	Py_DECREF(fob);
	if (!lineobj || !PyUnicode_Check(lineobj)) {
		Py_XDECREF(lineobj);
		return err;
	}

	/* remove the indentation of the line */
	u = PyUnicode_AS_UNICODE(lineobj);
	len = PyUnicode_GET_SIZE(lineobj);
	for (p=u; *p == ' ' || *p == '\t' || *p == '\014'; p++)
		len--;
	if (u != p) {
		PyObject *truncated;
		truncated = PyUnicode_FromUnicode(p, len);
		if (truncated) {
			Py_DECREF(lineobj);
			lineobj = truncated;
		} else {
			PyErr_Clear();
		}
	}

	/* Write some spaces before the line */
	strcpy(buf, "          ");
	assert (strlen(buf) == 10);
	while (indent > 0) {
		if(indent < 10)
			buf[indent] = '\0';
		err = PyFile_WriteString(buf, f);
		if (err != 0)
			break;
		indent -= 10;
	}

	/* finally display the line */
	if (err == 0)
		err = PyFile_WriteObject(lineobj, f, Py_PRINT_RAW);
	Py_DECREF(lineobj);
	if  (err == 0)
		err = PyFile_WriteString("\n", f);
	return err;
}

static int
tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
{
	int err = 0;
        char linebuf[2000];

	if (filename == NULL || name == NULL)
		return -1;
	/* This is needed by Emacs' compile command */
#define FMT "  File \"%.500s\", line %d, in %.500s\n"
	PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
	err = PyFile_WriteString(linebuf, f);
	if (err != 0)
		return err;
        return _Py_DisplaySourceLine(f, filename, lineno, 4);
}

static int
tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
{
	int err = 0;
	long 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;
	long 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;
}