Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b709df3

Browse files
committed
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.
1 parent b9ce5ad commit b709df3

5 files changed

Lines changed: 50 additions & 21 deletions

File tree

Doc/api/api.tex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,17 @@ \chapter{Exception Handling \label{exceptionHandling}}
972972
a dictionary of class variables and methods.
973973
\end{cfuncdesc}
974974

975+
\begin{cfuncdesc}{void}{PyErr_WriteUnraisable}{PyObject *obj}
976+
This utility function prints a warning message to \var{sys.stderr}
977+
when an exception has been set but it is impossible for the
978+
interpreter to actually raise the exception. It is used, for example,
979+
when an exception occurs in an \member{__del__} method.
980+
981+
The function is called with a single argument \var{obj} that
982+
identifies where the context in which the unraisable exception
983+
occurred. The repr of \var{obj} will be printed in the warning
984+
message.
985+
\end{cfuncdesc}
975986

976987
\section{Standard Exceptions \label{standardExceptions}}
977988

Include/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ extern DL_IMPORT(void) _PyErr_BadInternalCall(char *filename, int lineno);
9292
/* Function to create a new exception */
9393
DL_IMPORT(PyObject *) PyErr_NewException(char *name, PyObject *base,
9494
PyObject *dict);
95+
extern DL_IMPORT(void) PyErr_WriteUnraisable(PyObject *);
9596

9697
/* In sigcheck.c or signalmodule.c */
9798
extern DL_IMPORT(int) PyErr_CheckSignals(void);

Modules/gcmodule.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ static int allocated;
5757
DEBUG_UNCOLLECTABLE | \
5858
DEBUG_INSTANCES | \
5959
DEBUG_OBJECTS
60-
static int debug = 0;
60+
static int debug;
6161

6262
/* list of uncollectable objects */
6363
static PyObject *garbage;
6464

65+
/* Python string to use if unhandled exception occurs */
66+
static PyObject *gc_str;
6567

6668
/*** list functions ***/
6769

@@ -435,6 +437,10 @@ collect(PyGC_Head *young, PyGC_Head *old)
435437
* this if they insist on creating this type of structure. */
436438
handle_finalizers(&finalizers, old);
437439

440+
if (PyErr_Occurred()) {
441+
PyErr_WriteUnraisable(gc_str);
442+
Py_FatalError("unexpected exception during garbage collection");
443+
}
438444
allocated = 0;
439445
return n+m;
440446
}
@@ -699,6 +705,9 @@ initgc(void)
699705
if (garbage == NULL) {
700706
garbage = PyList_New(0);
701707
}
708+
if (gc_str == NULL) {
709+
gc_str = PyString_FromString("garbage collection");
710+
}
702711
PyDict_SetItemString(d, "garbage", garbage);
703712
PyDict_SetItemString(d, "DEBUG_STATS",
704713
PyInt_FromLong(DEBUG_STATS));

Objects/classobject.c

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -519,26 +519,7 @@ instance_dealloc(register PyInstanceObject *inst)
519519
if ((del = instance_getattr2(inst, delstr)) != NULL) {
520520
PyObject *res = PyEval_CallObject(del, (PyObject *)NULL);
521521
if (res == NULL) {
522-
PyObject *f, *t, *v, *tb;
523-
PyErr_Fetch(&t, &v, &tb);
524-
f = PySys_GetObject("stderr");
525-
if (f != NULL) {
526-
PyFile_WriteString("Exception ", f);
527-
if (t) {
528-
PyFile_WriteObject(t, f, Py_PRINT_RAW);
529-
if (v && v != Py_None) {
530-
PyFile_WriteString(": ", f);
531-
PyFile_WriteObject(v, f, 0);
532-
}
533-
}
534-
PyFile_WriteString(" in ", f);
535-
PyFile_WriteObject(del, f, 0);
536-
PyFile_WriteString(" ignored\n", f);
537-
PyErr_Clear(); /* Just in case */
538-
}
539-
Py_XDECREF(t);
540-
Py_XDECREF(v);
541-
Py_XDECREF(tb);
522+
PyErr_WriteUnraisable(del);
542523
}
543524
else
544525
Py_DECREF(res);

Python/errors.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,30 @@ PyErr_NewException(char *name, PyObject *base, PyObject *dict)
450450
Py_XDECREF(modulename);
451451
return result;
452452
}
453+
454+
/* Call when an exception has occurred but there is no way for Python
455+
to handle it. Examples: exception in __del__ or during GC. */
456+
void
457+
PyErr_WriteUnraisable(PyObject *obj)
458+
{
459+
PyObject *f, *t, *v, *tb;
460+
PyErr_Fetch(&t, &v, &tb);
461+
f = PySys_GetObject("stderr");
462+
if (f != NULL) {
463+
PyFile_WriteString("Exception ", f);
464+
if (t) {
465+
PyFile_WriteObject(t, f, Py_PRINT_RAW);
466+
if (v && v != Py_None) {
467+
PyFile_WriteString(": ", f);
468+
PyFile_WriteObject(v, f, 0);
469+
}
470+
}
471+
PyFile_WriteString(" in ", f);
472+
PyFile_WriteObject(obj, f, 0);
473+
PyFile_WriteString(" ignored\n", f);
474+
PyErr_Clear(); /* Just in case */
475+
}
476+
Py_XDECREF(t);
477+
Py_XDECREF(v);
478+
Py_XDECREF(tb);
479+
}

0 commit comments

Comments
 (0)