From 8246968b1222b25352d1aade43336686dc388221 Mon Sep 17 00:00:00 2001
From: Benjamin Peterson <benjamin@python.org>
Date: Wed, 9 Sep 2009 11:40:54 +0000
Subject: [PATCH] tabbify

---
 Lib/test/test_traceback.py | 44 ++++++++++++++++++
 Modules/pwdmodule.c        |  2 +-
 Python/traceback.c         | 94 ++++++++++++++++++++++++++++++++++----
 3 files changed, 131 insertions(+), 9 deletions(-)

diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index b29869ac8f..d7dcbacaf6 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -21,6 +21,50 @@ class TracebackCases(unittest.TestCase):
         else:
             raise ValueError, "call did not raise exception"
 
+    def test_tb_next(self):
+        def f():
+            raise Exception
+        def g():
+            f()
+        try:
+            g()
+        except Exception:
+            tb = sys.exc_info()[2]
+            self.assertEqual(tb.tb_frame.f_code.co_name, "test_tb_next")
+            g_tb = tb.tb_next
+            self.assertEqual(g_tb.tb_frame.f_code.co_name, "g")
+            f_tb = g_tb.tb_next
+            self.assertEqual(f_tb.tb_frame.f_code.co_name, "f")
+            self.assertIsNone(f_tb.tb_next)
+            tb.tb_next = None
+            self.assertIsNone(tb.tb_next)
+            self.assertRaises(ValueError, setattr, f_tb, "tb_next", g_tb)
+            self.assertRaises(TypeError, setattr, tb, "tb_next", 4)
+            g_tb.tb_next = None
+            f_tb.tb_next = g_tb
+            self.assertIs(f_tb.tb_next, g_tb)
+            self.assertRaises(ValueError, setattr, f_tb, "tb_next", f_tb)
+            self.assertRaises(TypeError, delattr, tb, "tb_next")
+
+    def test_tb_frame(self):
+        def f():
+            x = 2
+            raise Exception
+        try:
+            f()
+        except Exception:
+            tb = sys.exc_info()[2]
+            self.assertIs(sys._getframe(), tb.tb_frame)
+            f_tb = tb.tb_next
+            self.assertEqual(f_tb.tb_frame.f_code.co_name, "f")
+            self.assertEqual(f_tb.tb_frame.f_locals["x"], 2)
+            f_tb.tb_frame = None
+            self.assertIsNone(f_tb.tb_frame)
+            self.assertRaises(TypeError, setattr, t_tb, "tb_frame", 4)
+            self.assertRaises(TypeError, delattr, t_tb, "tb_frame")
+            t_tb.tb_frame = sys._getframe()
+            self.assertIs(t_tb.tb_frame, tb.tb_frame)
+
     def syntax_error_with_caret(self):
         compile("def fact(x):\n\treturn x!\n", "?", "exec")
 
diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c
index 2865dc6bf4..d1ce394149 100644
--- a/Modules/pwdmodule.c
+++ b/Modules/pwdmodule.c
@@ -194,7 +194,7 @@ initpwd(void)
 	Py_INCREF((PyObject *) &StructPwdType);
 	PyModule_AddObject(m, "struct_passwd", (PyObject *) &StructPwdType);
 	/* And for b/w compatibility (this was defined by mistake): */
-        Py_INCREF((PyObject *) &StructPwdType);
+	Py_INCREF((PyObject *) &StructPwdType);
 	PyModule_AddObject(m, "struct_pwent", (PyObject *) &StructPwdType);
 	initialized = 1;
 }
diff --git a/Python/traceback.c b/Python/traceback.c
index 1c26ba271a..b96ca47bc8 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -12,8 +12,6 @@
 #define OFF(x) offsetof(PyTracebackObject, x)
 
 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 */
@@ -45,6 +43,81 @@ tb_clear(PyTracebackObject *tb)
 	Py_CLEAR(tb->tb_frame);
 }
 
+static PyObject *
+tb_get_next(PyTracebackObject *tb) {
+	if (tb->tb_next) {
+		Py_INCREF(tb->tb_next);
+		return (PyObject *)tb->tb_next;
+	}
+	Py_RETURN_NONE;
+}
+
+static int
+tb_set_next(PyTracebackObject *tb, PyObject *new, void *context) {
+	if (!new) {
+		PyErr_SetString(PyExc_TypeError, "can't delete tb_next");
+		return -1;
+	}
+	else if (new == Py_None) {
+		new = NULL;
+	}
+	else if (PyTraceBack_Check(new)) {
+		/* Check for cycles in the traceback. */
+		PyTracebackObject *current = (PyTracebackObject *)new;
+		do {
+			if (tb == current) {
+				PyErr_SetString(PyExc_ValueError, "cycle in traceback");
+				return -1;
+			}
+		} while ((current = current->tb_next));
+		Py_INCREF(new);
+	}
+	else {
+		PyErr_SetString(PyExc_TypeError,
+				"tb_next must be an traceback object");
+		return -1;
+	}
+	Py_XDECREF(tb->tb_next);
+	tb->tb_next = (PyTracebackObject *)new;
+	return 0;
+}
+
+static PyObject *
+tb_get_frame(PyTracebackObject *tb) {
+	if (tb->tb_frame) {
+		Py_INCREF(tb->tb_frame);
+		return (PyObject *)tb->tb_frame;
+	}
+	Py_RETURN_NONE;
+}
+
+static int
+tb_set_frame(PyTracebackObject *tb, PyObject *new, void *context) {
+	if (!new) {
+		PyErr_SetString(PyExc_TypeError,
+				"can't delete tb_frame attribute");
+		return -1;
+	}
+	else if (new == Py_None) {
+		new = NULL;
+	}
+	else if (PyFrame_Check(new)) {
+		Py_INCREF(new);
+	}
+	else {
+		PyErr_SetString(PyExc_TypeError,
+				"tb_frame must be a frame object");
+		return -1;
+	}
+	tb->tb_frame = (PyFrameObject *)new;
+	return 0;
+}
+
+static PyGetSetDef tb_getset[] = {
+	{"tb_next", (getter)tb_get_next, (setter)tb_set_next, NULL},
+	{"tb_frame", (getter)tb_get_frame, (setter)tb_set_frame, NULL},
+};
+
 PyTypeObject PyTraceBack_Type = {
 	PyVarObject_HEAD_INIT(&PyType_Type, 0)
 	"traceback",
@@ -75,7 +148,7 @@ PyTypeObject PyTraceBack_Type = {
 	0,					/* tp_iternext */
 	0,					/* tp_methods */
 	tb_memberlist,			        /* tp_members */
-	0,			                /* tp_getset */
+	tb_getset,			        /* tp_getset */
 	0,					/* tp_base */
 	0,					/* tp_dict */
 };
@@ -245,11 +318,16 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
 	}
 	while (tb != NULL && err == 0) {
 		if (depth <= limit) {
-			err = tb_displayline(f,
-			    PyString_AsString(
-				    tb->tb_frame->f_code->co_filename),
-			    tb->tb_lineno,
-			    PyString_AsString(tb->tb_frame->f_code->co_name));
+			PyFrameObject *frame = tb->tb_frame;
+			char *filename, *name;
+			if (frame) {
+				filename = PyString_AS_STRING(frame->f_code->co_filename);
+				name = PyString_AS_STRING(frame->f_code->co_name);
+			}
+			else {
+				filename = name = "unkown (no frame on traceback)";
+			}
+			err = tb_displayline(f, filename, tb->tb_lineno, name);
 		}
 		depth--;
 		tb = tb->tb_next;
-- 
2.40.0