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.
This commit is contained in:
Guido van Rossum 2001-10-29 22:11:00 +00:00
parent c57a285cb4
commit 7ad2d1eb8e
2 changed files with 77 additions and 0 deletions
Misc
Objects

View File

@ -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).

View 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;