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.
This commit is contained in:
Jeremy Hylton 2000-09-01 02:47:25 +00:00
parent b9ce5ada37
commit b709df3810
5 changed files with 50 additions and 21 deletions

View File

@ -972,6 +972,17 @@ alternate base class. The \var{dict} argument can be used to specify
a dictionary of class variables and methods. a dictionary of class variables and methods.
\end{cfuncdesc} \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}} \section{Standard Exceptions \label{standardExceptions}}

View File

@ -92,6 +92,7 @@ extern DL_IMPORT(void) _PyErr_BadInternalCall(char *filename, int lineno);
/* Function to create a new exception */ /* Function to create a new exception */
DL_IMPORT(PyObject *) PyErr_NewException(char *name, PyObject *base, DL_IMPORT(PyObject *) PyErr_NewException(char *name, PyObject *base,
PyObject *dict); PyObject *dict);
extern DL_IMPORT(void) PyErr_WriteUnraisable(PyObject *);
/* In sigcheck.c or signalmodule.c */ /* In sigcheck.c or signalmodule.c */
extern DL_IMPORT(int) PyErr_CheckSignals(void); extern DL_IMPORT(int) PyErr_CheckSignals(void);

View File

@ -57,11 +57,13 @@ static int allocated;
DEBUG_UNCOLLECTABLE | \ DEBUG_UNCOLLECTABLE | \
DEBUG_INSTANCES | \ DEBUG_INSTANCES | \
DEBUG_OBJECTS DEBUG_OBJECTS
static int debug = 0; static int debug;
/* list of uncollectable objects */ /* list of uncollectable objects */
static PyObject *garbage; static PyObject *garbage;
/* Python string to use if unhandled exception occurs */
static PyObject *gc_str;
/*** list functions ***/ /*** list functions ***/
@ -435,6 +437,10 @@ collect(PyGC_Head *young, PyGC_Head *old)
* this if they insist on creating this type of structure. */ * this if they insist on creating this type of structure. */
handle_finalizers(&finalizers, old); handle_finalizers(&finalizers, old);
if (PyErr_Occurred()) {
PyErr_WriteUnraisable(gc_str);
Py_FatalError("unexpected exception during garbage collection");
}
allocated = 0; allocated = 0;
return n+m; return n+m;
} }
@ -699,6 +705,9 @@ initgc(void)
if (garbage == NULL) { if (garbage == NULL) {
garbage = PyList_New(0); garbage = PyList_New(0);
} }
if (gc_str == NULL) {
gc_str = PyString_FromString("garbage collection");
}
PyDict_SetItemString(d, "garbage", garbage); PyDict_SetItemString(d, "garbage", garbage);
PyDict_SetItemString(d, "DEBUG_STATS", PyDict_SetItemString(d, "DEBUG_STATS",
PyInt_FromLong(DEBUG_STATS)); PyInt_FromLong(DEBUG_STATS));

View File

@ -519,26 +519,7 @@ instance_dealloc(register PyInstanceObject *inst)
if ((del = instance_getattr2(inst, delstr)) != NULL) { if ((del = instance_getattr2(inst, delstr)) != NULL) {
PyObject *res = PyEval_CallObject(del, (PyObject *)NULL); PyObject *res = PyEval_CallObject(del, (PyObject *)NULL);
if (res == NULL) { if (res == NULL) {
PyObject *f, *t, *v, *tb; PyErr_WriteUnraisable(del);
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);
} }
else else
Py_DECREF(res); Py_DECREF(res);

View File

@ -450,3 +450,30 @@ PyErr_NewException(char *name, PyObject *base, PyObject *dict)
Py_XDECREF(modulename); Py_XDECREF(modulename);
return result; 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);
}