diff Objects/typeobject.c @ 20753:ecd64931482c legacy-trunk

Add __del__ callbacks. They are too useful to leave out. XXX Remaining problems: - The GC module doesn't know about these; I think it has its reasons to disallow calling __del__, but for now, __del__ on new-style objects is called when the GC module discards an object, for better or for worse. - The code to call a __del__ handler is really ridiculously complicated, due to all the different debug #ifdefs. I've copied this from the similar code in classobject.c, so I'm pretty sure I did it right, but it's not pretty. :-( - No tests yet.
author Guido van Rossum <guido@python.org>
date Mon, 29 Oct 2001 22:11:00 +0000
parents 00a581bdedeb
children 272336648d1f
line wrap: on
line diff
--- a/Objects/typeobject.c	Mon Oct 29 21:46:08 2001 +0000
+++ b/Objects/typeobject.c	Mon Oct 29 22:11:00 2001 +0000
@@ -230,6 +230,77 @@
 	return 0;
 }
 
+staticforward PyObject *lookup_maybe(PyObject *, char *, PyObject **);
+
+static int
+call_finalizer(PyObject *self)
+{
+	static PyObject *del_str = NULL;
+	PyObject *del, *res;
+	PyObject *error_type, *error_value, *error_traceback;
+
+	/* Temporarily resurrect the object. */
+#ifdef Py_TRACE_REFS
+#ifndef Py_REF_DEBUG
+#   error "Py_TRACE_REFS defined but Py_REF_DEBUG not."
+#endif
+	/* much too complicated if Py_TRACE_REFS defined */
+	_Py_NewReference((PyObject *)self);
+#ifdef COUNT_ALLOCS
+	/* compensate for boost in _Py_NewReference; note that
+	 * _Py_RefTotal was also boosted; we'll knock that down later.
+	 */
+	self->ob_type->tp_allocs--;
+#endif
+#else /* !Py_TRACE_REFS */
+	/* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */
+	Py_INCREF(self);
+#endif /* !Py_TRACE_REFS */
+
+	/* Save the current exception, if any. */
+	PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+	/* Execute __del__ method, if any. */
+	del = lookup_maybe(self, "__del__", &del_str);
+	if (del != NULL) {
+		res = PyEval_CallObject(del, NULL);
+		if (res == NULL)
+			PyErr_WriteUnraisable(del);
+		else
+			Py_DECREF(res);
+		Py_DECREF(del);
+	}
+
+	/* Restore the saved exception. */
+	PyErr_Restore(error_type, error_value, error_traceback);
+
+	/* Undo the temporary resurrection; can't use DECREF here, it would
+	 * cause a recursive call.
+	 */
+#ifdef Py_REF_DEBUG
+	/* _Py_RefTotal was boosted either by _Py_NewReference or
+	 * Py_INCREF above.
+	 */
+	_Py_RefTotal--;
+#endif
+	if (--self->ob_refcnt > 0) {
+#ifdef COUNT_ALLOCS
+		self->ob_type->tp_frees--;
+#endif
+		_PyObject_GC_TRACK(self);
+		return -1; /* __del__ added a reference; don't delete now */
+	}
+#ifdef Py_TRACE_REFS
+	_Py_ForgetReference((PyObject *)self);
+#ifdef COUNT_ALLOCS
+	/* compensate for increment in _Py_ForgetReference */
+	self->ob_type->tp_frees--;
+#endif
+#endif
+
+	return 0;
+}
+
 static void
 subtype_dealloc(PyObject *self)
 {
@@ -238,6 +309,9 @@
 
 	/* This exists so we can DECREF self->ob_type */
 
+	if (call_finalizer(self) < 0)
+		return;
+
 	/* Find the nearest base with a different tp_dealloc */
 	type = self->ob_type;
 	base = type->tp_base;