mirror of https://github.com/python/cpython.git
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:
parent
c57a285cb4
commit
7ad2d1eb8e
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue