From b709df381034b6055f03644a8f2eb35cfc6cb411 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 1 Sep 2000 02:47:25 +0000 Subject: refactor __del__ exception handler into PyErr_WriteUnraisable add sanity check to gc: if an exception occurs during GC, call PyErr_WriteUnraisable and then call Py_FatalEror. --- Doc/api/api.tex | 11 +++++++++++ Include/pyerrors.h | 1 + Modules/gcmodule.c | 11 ++++++++++- Objects/classobject.c | 21 +-------------------- Python/errors.c | 27 +++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Doc/api/api.tex b/Doc/api/api.tex index 1f20129..c8731c9 100644 --- a/Doc/api/api.tex +++ b/Doc/api/api.tex @@ -972,6 +972,17 @@ alternate base class. The \var{dict} argument can be used to specify a dictionary of class variables and methods. \end{cfuncdesc} +\begin{cfuncdesc}{void}{PyErr_WriteUnraisable}{PyObject *obj} +This utility function prints a warning message to \var{sys.stderr} +when an exception has been set but it is impossible for the +interpreter to actually raise the exception. It is used, for example, +when an exception occurs in an \member{__del__} method. + +The function is called with a single argument \var{obj} that +identifies where the context in which the unraisable exception +occurred. The repr of \var{obj} will be printed in the warning +message. +\end{cfuncdesc} \section{Standard Exceptions \label{standardExceptions}} diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 6e60353..311e258 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -92,6 +92,7 @@ extern DL_IMPORT(void) _PyErr_BadInternalCall(char *filename, int lineno); /* Function to create a new exception */ DL_IMPORT(PyObject *) PyErr_NewException(char *name, PyObject *base, PyObject *dict); +extern DL_IMPORT(void) PyErr_WriteUnraisable(PyObject *); /* In sigcheck.c or signalmodule.c */ extern DL_IMPORT(int) PyErr_CheckSignals(void); diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 57ee7b9..889ae25 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -57,11 +57,13 @@ static int allocated; DEBUG_UNCOLLECTABLE | \ DEBUG_INSTANCES | \ DEBUG_OBJECTS -static int debug = 0; +static int debug; /* list of uncollectable objects */ static PyObject *garbage; +/* Python string to use if unhandled exception occurs */ +static PyObject *gc_str; /*** list functions ***/ @@ -435,6 +437,10 @@ collect(PyGC_Head *young, PyGC_Head *old) * this if they insist on creating this type of structure. */ handle_finalizers(&finalizers, old); + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(gc_str); + Py_FatalError("unexpected exception during garbage collection"); + } allocated = 0; return n+m; } @@ -699,6 +705,9 @@ initgc(void) if (garbage == NULL) { garbage = PyList_New(0); } + if (gc_str == NULL) { + gc_str = PyString_FromString("garbage collection"); + } PyDict_SetItemString(d, "garbage", garbage); PyDict_SetItemString(d, "DEBUG_STATS", PyInt_FromLong(DEBUG_STATS)); diff --git a/Objects/classobject.c b/Objects/classobject.c index 0b595b9..615c8ba 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -519,26 +519,7 @@ instance_dealloc(register PyInstanceObject *inst) if ((del = instance_getattr2(inst, delstr)) != NULL) { PyObject *res = PyEval_CallObject(del, (PyObject *)NULL); if (res == NULL) { - PyObject *f, *t, *v, *tb; - PyErr_Fetch(&t, &v, &tb); - f = PySys_GetObject("stderr"); - if (f != NULL) { - PyFile_WriteString("Exception ", f); - if (t) { - PyFile_WriteObject(t, f, Py_PRINT_RAW); - if (v && v != Py_None) { - PyFile_WriteString(": ", f); - PyFile_WriteObject(v, f, 0); - } - } - PyFile_WriteString(" in ", f); - PyFile_WriteObject(del, f, 0); - PyFile_WriteString(" ignored\n", f); - PyErr_Clear(); /* Just in case */ - } - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); + PyErr_WriteUnraisable(del); } else Py_DECREF(res); diff --git a/Python/errors.c b/Python/errors.c index 8486423..355ec9c 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -450,3 +450,30 @@ PyErr_NewException(char *name, PyObject *base, PyObject *dict) Py_XDECREF(modulename); return result; } + +/* Call when an exception has occurred but there is no way for Python + to handle it. Examples: exception in __del__ or during GC. */ +void +PyErr_WriteUnraisable(PyObject *obj) +{ + PyObject *f, *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + f = PySys_GetObject("stderr"); + if (f != NULL) { + PyFile_WriteString("Exception ", f); + if (t) { + PyFile_WriteObject(t, f, Py_PRINT_RAW); + if (v && v != Py_None) { + PyFile_WriteString(": ", f); + PyFile_WriteObject(v, f, 0); + } + } + PyFile_WriteString(" in ", f); + PyFile_WriteObject(obj, f, 0); + PyFile_WriteString(" ignored\n", f); + PyErr_Clear(); /* Just in case */ + } + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); +} -- cgit v0.12