Add sys.excepthook.
authorKa-Ping Yee <ping@zesty.ca>
Fri, 23 Mar 2001 02:46:52 +0000 (02:46 +0000)
committerKa-Ping Yee <ping@zesty.ca>
Fri, 23 Mar 2001 02:46:52 +0000 (02:46 +0000)
Update docstring and library reference section on 'sys' module.
New API PyErr_Display, just for displaying errors, called by excepthook.
Uncaught exceptions now call sys.excepthook; if that fails, we fall back
    to calling PyErr_Display directly.
Also comes with sys.__excepthook__ and sys.__displayhook__.

Doc/lib/libsys.tex
Include/pythonrun.h
Python/pythonrun.c
Python/sysmodule.c

index df32182e666b7f3e7acd3f201bb4ca360e69d1b2..44b785373866b1bdc1e4bdb617c4e4046481df92 100644 (file)
@@ -48,11 +48,32 @@ Availability: Windows.
 If \var{value} is not \code{None}, this function prints it to 
 \code{sys.stdout}, and saves it in \code{__builtin__._}.
 
-This function is called when an expression is entered at the prompt
-of an interactive Python session. It exists mainly so it can be
-overridden.
+\code{sys.displayhook} is called on the result of evaluating
+an expression entered in an interactive Python session.
+The display of these values can be customized by assigning
+another function to \code{sys.displayhook}.
 \end{funcdesc}
 
+\begin{funcdesc}{excepthook}{\var{type}, \var{value}, \var{traceback}}
+This function prints out a given traceback and exception to
+\code{sys.stderr}.
+
+\code{sys.excepthook} is called when an exception is raised
+and uncaught.  In an interactive session this happens just before
+control is returned to the prompt; in a Python program this happens
+just before the program exits.
+The handling of such top-level exceptions can be customized by
+assigning another function to \code{sys.excepthook}.
+\end{funcdesc}
+
+\begin{datadesc}{__displayhook__}
+\dataline{__excepthook__}
+These objects contain the original values of \code{displayhook}
+and \code{excepthook} at the start of the program.  They are saved
+so that \code{displayhook} and \code{excepthook} can be restored
+in case they happen to get replaced with broken objects.
+\end{datadesc}
+
 \begin{funcdesc}{exc_info}{}
 This function returns a tuple of three values that give information
 about the exception that is currently being handled.  The information
index c2aa2073c738174c353cec4f05f6c3db7478de5d..e7274ec489f384565820f25bbac674abdc606892 100644 (file)
@@ -59,6 +59,7 @@ DL_IMPORT(struct symtable *) Py_SymtableString(char *, char *, int);
 
 DL_IMPORT(void) PyErr_Print(void);
 DL_IMPORT(void) PyErr_PrintEx(int);
+DL_IMPORT(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
 
 DL_IMPORT(int) Py_AtExit(void (*func)(void));
 
index edb8a1102aa09f6ddbcbf0de3320eb4244f642cf..6fa85ac08cd0479d542f75a95d58cc846c96d501 100644 (file)
@@ -801,8 +801,7 @@ print_error_text(PyObject *f, int offset, char *text)
 void
 PyErr_PrintEx(int set_sys_last_vars)
 {
-       int err = 0;
-       PyObject *exception, *v, *tb, *f;
+       PyObject *exception, *v, *tb, *hook;
        PyErr_Fetch(&exception, &v, &tb);
        PyErr_NormalizeException(&exception, &v, &tb);
 
@@ -845,14 +844,47 @@ PyErr_PrintEx(int set_sys_last_vars)
                PySys_SetObject("last_value", v);
                PySys_SetObject("last_traceback", tb);
        }
-       f = PySys_GetObject("stderr");
+       hook = PySys_GetObject("excepthook");
+       if (hook) {
+               PyObject *args = Py_BuildValue("(OOO)",
+                    exception, v ? v : Py_None, tb ? tb : Py_None);
+               PyObject *result = PyEval_CallObject(hook, args);
+               if (result == NULL) {
+                       PyObject *exception2, *v2, *tb2;
+                       PyErr_Fetch(&exception2, &v2, &tb2);
+                       PyErr_NormalizeException(&exception2, &v2, &tb2);
+                       if (Py_FlushLine())
+                               PyErr_Clear();
+                       fflush(stdout);
+                       PySys_WriteStderr("Error in sys.excepthook:\n");
+                       PyErr_Display(exception2, v2, tb2);
+                       PySys_WriteStderr("\nOriginal exception was:\n");
+                       PyErr_Display(exception, v, tb);
+               }
+               Py_XDECREF(result);
+               Py_XDECREF(args);
+       } else {
+               PySys_WriteStderr("sys.excepthook is missing\n");
+               PyErr_Display(exception, v, tb);
+       }
+       Py_XDECREF(exception);
+       Py_XDECREF(v);
+       Py_XDECREF(tb);
+}
+
+void PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+{
+       int err = 0;
+       PyObject *v = value;
+       PyObject *f = PySys_GetObject("stderr");
        if (f == NULL)
                fprintf(stderr, "lost sys.stderr\n");
        else {
                if (Py_FlushLine())
                        PyErr_Clear();
                fflush(stdout);
-               err = PyTraceBack_Print(tb, f);
+               if (tb && tb != Py_None)
+                       err = PyTraceBack_Print(tb, f);
                if (err == 0 &&
                    PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError))
                {
@@ -875,8 +907,6 @@ PyErr_PrintEx(int set_sys_last_vars)
                                PyFile_WriteString("\n", f);
                                if (text != NULL)
                                        print_error_text(f, offset, text);
-                               Py_INCREF(message);
-                               Py_DECREF(v);
                                v = message;
                                /* Can't be bothered to check all those
                                   PyFile_WriteString() calls */
@@ -932,9 +962,6 @@ PyErr_PrintEx(int set_sys_last_vars)
                if (err == 0)
                        err = PyFile_WriteString("\n", f);
        }
-       Py_XDECREF(exception);
-       Py_XDECREF(v);
-       Py_XDECREF(tb);
        /* If an error happened here, don't show it.
           XXX This is wrong, but too many callers rely on this behavior. */
        if (err != 0)
index 3df85ff02289297233d53fb9d400c97f61639a53..9a0a43b6827a923072fbb89b7804c2f1082e5051 100644 (file)
@@ -107,9 +107,25 @@ sys_displayhook(PyObject *self, PyObject *args)
 }
 
 static char displayhook_doc[] =
-"displayhook(o) -> None\n"
+"displayhook(object) -> None\n"
 "\n"
-"Print o to the stdout, and save it in __builtin__._\n";
+"Print an object to sys.stdout and also save it in __builtin__._\n";
+
+static PyObject *
+sys_excepthook(PyObject* self, PyObject* args)
+{
+       PyObject *exc, *value, *tb;
+       if (!PyArg_ParseTuple(args, "OOO:excepthook", &exc, &value, &tb))
+               return NULL;
+       PyErr_Display(exc, value, tb);
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static char excepthook_doc[] =
+"excepthook(exctype, value, traceback) -> None\n"
+"\n"
+"Handle an exception by displaying it with a traceback on sys.stderr.\n";
 
 static PyObject *
 sys_exc_info(PyObject *self, PyObject *args)
@@ -378,6 +394,7 @@ static PyMethodDef sys_methods[] = {
        /* Might as well keep this in alphabetic order */
        {"displayhook", sys_displayhook, 1, displayhook_doc},
        {"exc_info",    sys_exc_info, 1, exc_info_doc},
+       {"excepthook",  sys_excepthook, 1, excepthook_doc},
        {"exit",        sys_exit, 0, exit_doc},
        {"getdefaultencoding", sys_getdefaultencoding, 1,
         getdefaultencoding_doc}, 
@@ -477,13 +494,20 @@ Dynamic objects:\n\
 argv -- command line arguments; argv[0] is the script pathname if known\n\
 path -- module search path; path[0] is the script directory, else ''\n\
 modules -- dictionary of loaded modules\n\
-exitfunc -- you may set this to a function to be called when Python exits\n\
+\n\
+displayhook -- called to show results in an interactive session\n\
+excepthook -- called to handle any uncaught exception other than SystemExit\n\
+  To customize printing in an interactive session or to install a custom\n\
+  top-level exception handler, assign other functions to replace these.\n\
+\n\
+exitfunc -- if sys.exitfunc exists, this routine is called when Python exits\n\
+  Assigning to sys.exitfunc is deprecated; use the atexit module instead.\n\
 \n\
 stdin -- standard input file object; used by raw_input() and input()\n\
 stdout -- standard output file object; used by the print statement\n\
 stderr -- standard error object; used for error messages\n\
-  By assigning another file object (or an object that behaves like a file)\n\
-  to one of these, it is possible to redirect all of the interpreter's I/O.\n\
+  By assigning other file objects (or objects that behave like files)\n\
+  to these, it is possible to redirect all of the interpreter's I/O.\n\
 \n\
 last_type -- type of last uncaught exception\n\
 last_value -- value of last uncaught exception\n\
@@ -498,7 +522,7 @@ exc_traceback -- traceback of exception currently being handled\n\
   because it is thread-safe.\n\
 "
 #ifndef MS_WIN16
-/* Concatenating string here */
+/* concatenating string here */
 "\n\
 Static objects:\n\
 \n\
@@ -512,15 +536,23 @@ platform -- platform identifier\n\
 executable -- pathname of this Python interpreter\n\
 prefix -- prefix used to find the Python library\n\
 exec_prefix -- prefix used to find the machine-specific Python library\n\
-dllhandle -- [Windows only] integer handle of the Python DLL\n\
+"
+#ifdef MS_WINDOWS
+/* concatenating string here */
+"dllhandle -- [Windows only] integer handle of the Python DLL\n\
 winver -- [Windows only] version number of the Python DLL\n\
-__stdin__ -- the original stdin; don't use!\n\
-__stdout__ -- the original stdout; don't use!\n\
-__stderr__ -- the original stderr; don't use!\n\
+"
+#endif /* MS_WINDOWS */
+"__stdin__ -- the original stdin; don't touch!\n\
+__stdout__ -- the original stdout; don't touch!\n\
+__stderr__ -- the original stderr; don't touch!\n\
+__displayhook__ -- the original displayhook; don't touch!\n\
+__excepthook__ -- the original excepthook; don't touch!\n\
 \n\
 Functions:\n\
 \n\
 displayhook() -- print an object to the screen, and save it in __builtin__._\n\
+excepthook() -- print an exception and its traceback to sys.stderr\n\
 exc_info() -- return thread-safe information about the current exception\n\
 exit() -- exit the interpreter by raising SystemExit\n\
 getrefcount() -- return the reference count for an object (plus one :-)\n\
@@ -530,7 +562,7 @@ setprofile() -- set the global profiling function\n\
 setrecursionlimit() -- set the max recursion depth for the interpreter\n\
 settrace() -- set the global debug tracing function\n\
 "
-#endif
+#endif /* MS_WIN16 */
 /* end of sys_doc */ ;
 
 PyObject *
@@ -555,6 +587,10 @@ _PySys_Init(void)
        PyDict_SetItemString(sysdict, "__stdin__", sysin);
        PyDict_SetItemString(sysdict, "__stdout__", sysout);
        PyDict_SetItemString(sysdict, "__stderr__", syserr);
+       PyDict_SetItemString(sysdict, "__displayhook__",
+                             PyDict_GetItemString(sysdict, "displayhook"));
+       PyDict_SetItemString(sysdict, "__excepthook__",
+                             PyDict_GetItemString(sysdict, "excepthook"));
        Py_XDECREF(sysin);
        Py_XDECREF(sysout);
        Py_XDECREF(syserr);