From b5c5132d1ac526dc97f8c51ef12299bde791a807 Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Fri, 23 Mar 2001 02:46:52 +0000 Subject: Add sys.excepthook. 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 | 27 ++++++++++++++++++++++--- Include/pythonrun.h | 1 + Python/pythonrun.c | 45 ++++++++++++++++++++++++++++++++--------- Python/sysmodule.c | 58 +++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 108 insertions(+), 23 deletions(-) diff --git a/Doc/lib/libsys.tex b/Doc/lib/libsys.tex index df32182..44b7853 100644 --- a/Doc/lib/libsys.tex +++ b/Doc/lib/libsys.tex @@ -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 diff --git a/Include/pythonrun.h b/Include/pythonrun.h index c2aa207..e7274ec 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -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)); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index edb8a11..6fa85ac 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -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) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 3df85ff..9a0a43b 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -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); -- cgit v0.12