summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2008-07-15 15:32:09 (GMT)
committerBenjamin Peterson <benjamin@python.org>2008-07-15 15:32:09 (GMT)
commite65282114e96efb9e7eee77c57244943b746f6fe (patch)
tree9daa571b35e287058d54b39bffbc6ca89a843f65 /Python
parent9bab65c25928617b2911d2f42d01e5543893ed93 (diff)
downloadcpython-e65282114e96efb9e7eee77c57244943b746f6fe.zip
cpython-e65282114e96efb9e7eee77c57244943b746f6fe.tar.gz
cpython-e65282114e96efb9e7eee77c57244943b746f6fe.tar.bz2
implement chained exception tracebacks
patch from Antoine Pitrou #3112
Diffstat (limited to 'Python')
-rw-r--r--Python/_warnings.c3
-rw-r--r--Python/errors.c22
-rw-r--r--Python/pythonrun.c251
-rw-r--r--Python/traceback.c27
4 files changed, 202 insertions, 101 deletions
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 23223fa..e9384ca 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -256,7 +256,6 @@ show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
Py_XDECREF(name);
/* Print " source_line\n" */
- PyFile_WriteString(" ", f_stderr);
if (sourceline) {
char *source_line_str = PyUnicode_AsString(sourceline);
while (*source_line_str == ' ' || *source_line_str == '\t' ||
@@ -267,7 +266,7 @@ show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
PyFile_WriteString("\n", f_stderr);
}
else
- Py_DisplaySourceLine(f_stderr, PyUnicode_AsString(filename), lineno);
+ Py_DisplaySourceLine(f_stderr, PyUnicode_AsString(filename), lineno, 2);
PyErr_Clear();
}
diff --git a/Python/errors.c b/Python/errors.c
index ac64b6a..3b86c48 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -84,8 +84,23 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
return;
value = fixed_value;
}
- Py_INCREF(tstate->exc_value);
- PyException_SetContext(value, tstate->exc_value);
+ /* Avoid reference cycles through the context chain.
+ This is O(chain length) but context chains are
+ usually very short. Sensitive readers may try
+ to inline the call to PyException_GetContext. */
+ if (tstate->exc_value != value) {
+ PyObject *o = tstate->exc_value, *context;
+ while ((context = PyException_GetContext(o))) {
+ Py_DECREF(context);
+ if (context == value) {
+ PyException_SetContext(o, NULL);
+ break;
+ }
+ o = context;
+ }
+ Py_INCREF(tstate->exc_value);
+ PyException_SetContext(value, tstate->exc_value);
+ }
}
if (value != NULL && PyExceptionInstance_Check(value))
tb = PyException_GetTraceback(value);
@@ -160,6 +175,9 @@ PyErr_ExceptionMatches(PyObject *exc)
/* Used in many places to normalize a raised exception, including in
eval_code2(), do_raise(), and PyErr_Print()
+
+ XXX: should PyErr_NormalizeException() also call
+ PyException_SetTraceback() with the resulting value and tb?
*/
void
PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index c46e9f4..ad758a6 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1242,18 +1242,19 @@ PyErr_PrintEx(int set_sys_last_vars)
if (exception == NULL)
return;
PyErr_NormalizeException(&exception, &v, &tb);
+ tb = tb ? tb : Py_None;
+ PyException_SetTraceback(v, tb);
if (exception == NULL)
return;
/* Now we know v != NULL too */
if (set_sys_last_vars) {
PySys_SetObject("last_type", exception);
PySys_SetObject("last_value", v);
- PySys_SetObject("last_traceback", tb ? tb : Py_None);
+ PySys_SetObject("last_traceback", tb);
}
hook = PySys_GetObject("excepthook");
if (hook) {
- PyObject *args = PyTuple_Pack(3,
- exception, v, tb ? tb : Py_None);
+ PyObject *args = PyTuple_Pack(3, exception, v, tb);
PyObject *result = PyEval_CallObject(hook, args);
if (result == NULL) {
PyObject *exception2, *v2, *tb2;
@@ -1293,106 +1294,100 @@ PyErr_PrintEx(int set_sys_last_vars)
Py_XDECREF(tb);
}
-void
-PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+static void
+print_exception(PyObject *f, PyObject *value)
{
int err = 0;
- PyObject *f = PySys_GetObject("stderr");
+ PyObject *type, *tb;
+
Py_INCREF(value);
- if (f == Py_None) {
- /* pass */
+ fflush(stdout);
+ type = (PyObject *) Py_TYPE(value);
+ tb = PyException_GetTraceback(value);
+ if (tb && tb != Py_None)
+ err = PyTraceBack_Print(tb, f);
+ if (err == 0 &&
+ PyObject_HasAttrString(value, "print_file_and_line"))
+ {
+ PyObject *message;
+ const char *filename, *text;
+ int lineno, offset;
+ if (!parse_syntax_error(value, &message, &filename,
+ &lineno, &offset, &text))
+ PyErr_Clear();
+ else {
+ char buf[10];
+ PyFile_WriteString(" File \"", f);
+ if (filename == NULL)
+ PyFile_WriteString("<string>", f);
+ else
+ PyFile_WriteString(filename, f);
+ PyFile_WriteString("\", line ", f);
+ PyOS_snprintf(buf, sizeof(buf), "%d", lineno);
+ PyFile_WriteString(buf, f);
+ PyFile_WriteString("\n", f);
+ if (text != NULL)
+ print_error_text(f, offset, text);
+ Py_DECREF(value);
+ value = message;
+ /* Can't be bothered to check all those
+ PyFile_WriteString() calls */
+ if (PyErr_Occurred())
+ err = -1;
+ }
}
- else if (f == NULL) {
- _PyObject_Dump(value);
- fprintf(stderr, "lost sys.stderr\n");
+ if (err) {
+ /* Don't do anything else */
}
else {
- fflush(stdout);
- if (tb && tb != Py_None)
- err = PyTraceBack_Print(tb, f);
- if (err == 0 &&
- PyObject_HasAttrString(value, "print_file_and_line"))
- {
- PyObject *message;
- const char *filename, *text;
- int lineno, offset;
- if (!parse_syntax_error(value, &message, &filename,
- &lineno, &offset, &text))
- PyErr_Clear();
- else {
- char buf[10];
- PyFile_WriteString(" File \"", f);
- if (filename == NULL)
- PyFile_WriteString("<string>", f);
- else
- PyFile_WriteString(filename, f);
- PyFile_WriteString("\", line ", f);
- PyOS_snprintf(buf, sizeof(buf), "%d", lineno);
- PyFile_WriteString(buf, f);
- PyFile_WriteString("\n", f);
- if (text != NULL)
- print_error_text(f, offset, text);
- Py_DECREF(value);
- value = message;
- /* Can't be bothered to check all those
- PyFile_WriteString() calls */
- if (PyErr_Occurred())
- err = -1;
- }
+ assert(PyExceptionClass_Check(type));
+ PyObject* moduleName;
+ char* className = PyExceptionClass_Name(type);
+ if (className != NULL) {
+ char *dot = strrchr(className, '.');
+ if (dot != NULL)
+ className = dot+1;
}
- if (err) {
- /* Don't do anything else */
- }
- else if (PyExceptionClass_Check(exception)) {
- PyObject* moduleName;
- char* className = PyExceptionClass_Name(exception);
- if (className != NULL) {
- char *dot = strrchr(className, '.');
- if (dot != NULL)
- className = dot+1;
- }
- moduleName = PyObject_GetAttrString(exception, "__module__");
- if (moduleName == NULL || !PyUnicode_Check(moduleName))
+ moduleName = PyObject_GetAttrString(type, "__module__");
+ if (moduleName == NULL || !PyUnicode_Check(moduleName))
+ {
+ Py_DECREF(moduleName);
+ err = PyFile_WriteString("<unknown>", f);
+ }
+ else {
+ char* modstr = PyUnicode_AsString(moduleName);
+ if (modstr && strcmp(modstr, "builtins"))
{
- Py_DECREF(moduleName);
- err = PyFile_WriteString("<unknown>", f);
- }
- else {
- char* modstr = PyUnicode_AsString(moduleName);
- if (modstr && strcmp(modstr, "builtins"))
- {
- err = PyFile_WriteString(modstr, f);
- err += PyFile_WriteString(".", f);
- }
- Py_DECREF(moduleName);
- }
- if (err == 0) {
- if (className == NULL)
- err = PyFile_WriteString("<unknown>", f);
- else
- err = PyFile_WriteString(className, f);
+ err = PyFile_WriteString(modstr, f);
+ err += PyFile_WriteString(".", f);
}
+ Py_DECREF(moduleName);
}
- else
- err = PyFile_WriteObject(exception, f, Py_PRINT_RAW);
- if (err == 0 && (value != Py_None)) {
- PyObject *s = PyObject_Str(value);
- /* only print colon if the str() of the
- object is not the empty string
- */
- if (s == NULL)
- err = -1;
- else if (!PyUnicode_Check(s) ||
- PyUnicode_GetSize(s) != 0)
- err = PyFile_WriteString(": ", f);
- if (err == 0)
- err = PyFile_WriteObject(s, f, Py_PRINT_RAW);
- Py_XDECREF(s);
+ if (err == 0) {
+ if (className == NULL)
+ err = PyFile_WriteString("<unknown>", f);
+ else
+ err = PyFile_WriteString(className, f);
}
- /* try to write a newline in any case */
- err += PyFile_WriteString("\n", f);
}
+ if (err == 0 && (value != Py_None)) {
+ PyObject *s = PyObject_Str(value);
+ /* only print colon if the str() of the
+ object is not the empty string
+ */
+ if (s == NULL)
+ err = -1;
+ else if (!PyUnicode_Check(s) ||
+ PyUnicode_GetSize(s) != 0)
+ err = PyFile_WriteString(": ", f);
+ if (err == 0)
+ err = PyFile_WriteObject(s, f, Py_PRINT_RAW);
+ Py_XDECREF(s);
+ }
+ /* try to write a newline in any case */
+ err += PyFile_WriteString("\n", f);
+ Py_XDECREF(tb);
Py_DECREF(value);
/* If an error happened here, don't show it.
XXX This is wrong, but too many callers rely on this behavior. */
@@ -1400,6 +1395,82 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
PyErr_Clear();
}
+static const char *cause_message =
+ "\nThe above exception was the direct cause "
+ "of the following exception:\n\n";
+
+static const char *context_message =
+ "\nDuring handling of the above exception, "
+ "another exception occurred:\n\n";
+
+static void
+print_exception_recursive(PyObject *f, PyObject *value, PyObject *seen)
+{
+ int err = 0, res;
+ PyObject *cause, *context;
+
+ if (seen != NULL) {
+ /* Exception chaining */
+ if (PySet_Add(seen, value) == -1)
+ PyErr_Clear();
+ else if (PyExceptionInstance_Check(value)) {
+ cause = PyException_GetCause(value);
+ context = PyException_GetContext(value);
+ if (cause) {
+ res = PySet_Contains(seen, cause);
+ if (res == -1)
+ PyErr_Clear();
+ if (res == 0) {
+ print_exception_recursive(
+ f, cause, seen);
+ err |= PyFile_WriteString(
+ cause_message, f);
+ }
+ }
+ if (context) {
+ res = PySet_Contains(seen, context);
+ if (res == -1)
+ PyErr_Clear();
+ if (res == 0) {
+ print_exception_recursive(
+ f, context, seen);
+ err |= PyFile_WriteString(
+ context_message, f);
+ }
+ }
+ Py_XDECREF(context);
+ Py_XDECREF(cause);
+ }
+ }
+ print_exception(f, value);
+ if (err != 0)
+ PyErr_Clear();
+}
+
+void
+PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+{
+ PyObject *seen;
+ PyObject *f = PySys_GetObject("stderr");
+ if (f == Py_None) {
+ /* pass */
+ }
+ else if (f == NULL) {
+ _PyObject_Dump(value);
+ fprintf(stderr, "lost sys.stderr\n");
+ }
+ else {
+ /* We choose to ignore seen being possibly NULL, and report
+ at least the main exception (it could be a MemoryError).
+ */
+ seen = PySet_New(NULL);
+ if (seen == NULL)
+ PyErr_Clear();
+ print_exception_recursive(f, value, seen);
+ Py_XDECREF(seen);
+ }
+}
+
PyObject *
PyRun_StringFlags(const char *str, int start, PyObject *globals,
PyObject *locals, PyCompilerFlags *flags)
diff --git a/Python/traceback.c b/Python/traceback.c
index 76e22a1..55300fc 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -129,7 +129,7 @@ PyTraceBack_Here(PyFrameObject *frame)
}
int
-Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno)
+Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno, int indent)
{
int err = 0;
FILE *xfp = NULL;
@@ -139,8 +139,6 @@ Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno)
if (filename == 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 */
@@ -203,12 +201,27 @@ Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno)
} while (*pLastChar != '\0' && *pLastChar != '\n');
}
if (i == lineno) {
+ char buf[11];
char *p = linebuf;
while (*p == ' ' || *p == '\t' || *p == '\014')
p++;
- err = PyFile_WriteString(p, f);
- if (err == 0 && strchr(p, '\n') == NULL)
- err = PyFile_WriteString("\n", f);
+
+ /* 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;
+ }
+
+ if (err == 0)
+ err = PyFile_WriteString(p, f);
+ if (err == 0 && strchr(p, '\n') == NULL)
+ err = PyFile_WriteString("\n", f);
}
fclose(xfp);
return err;
@@ -228,7 +241,7 @@ tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
err = PyFile_WriteString(linebuf, f);
if (err != 0)
return err;
- return Py_DisplaySourceLine(f, filename, lineno);
+ return Py_DisplaySourceLine(f, filename, lineno, 4);
}
static int