]> granicus.if.org Git - python/commitdiff
Add __del__ callbacks. They are too useful to leave out.
authorGuido van Rossum <guido@python.org>
Mon, 29 Oct 2001 22:11:00 +0000 (22:11 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 29 Oct 2001 22:11:00 +0000 (22:11 +0000)
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.

Misc/NEWS
Objects/typeobject.c

index 2a29558ee20acc472ecfefdaffe0b92777a7169c..55e916b2bfa385729d53f86023c531836378f8b3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -4,6 +4,9 @@ XXX Planned XXX Release date: 14-Nov-2001
 
 Type/class unification and new-style classes
 
+- New-style classes can now have a __del__ method, which is called
+  when the instance is deleted (just like for classic classes).
+
 - Assignment to object.__dict__ is now possible, for objects that are
   instances of new-style classes that have a __dict__ (unless the base
   class forbids it).
index d5f6a8f0fe6e9e4d39b9a54b4c8341d5739000e0..b7abba9cd32c1285c88bf43214eeee4d8d87db02 100644 (file)
@@ -230,6 +230,77 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
        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 @@ subtype_dealloc(PyObject *self)
 
        /* 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;