[Python-Dev] PEP 442 clarification for type hierarchies
Antoine Pitrou
solipsis at pitrou.net
Mon Aug 5 21:26:30 CEST 2013
On Mon, 05 Aug 2013 21:03:33 +0200
Stefan Behnel <stefan_ml at behnel.de> wrote:
> I think the main problem I have with the PEP is this part:
>
> """
> The PEP doesn't change the semantics of:
> * C extension types with a custom tp_dealloc function.
> """
>
> Meaning, it was designed to explicitly ignore this use case.
It doesn't ignore it. It lets you fix your C extension type to use
tp_finalize for resource finalization. It also provides the
PyObject_CallFinalizerFromDealloc() API function to make it easier to
call tp_finalize from your tp_dealloc.
What the above sentence means is that, if you don't change your legacy
tp_dealloc, your type will not take advantage of the new facilities.
(you can take a look at the _io module; it was modified to take
advantage of tp_finalize)
> * make finalisation run recursively (or iteratively) through all
> inheritance levels, in a well defined execution environment (e.g. after
> saving away the exception state)
__init__ and other methods only let the user recurse explicitly.
__del__ would be a weird exception if it recursed implicitly. Also, it
would break backwards compatibility for existing uses of __del__.
> I think it's a mistake that the current implementation calls the
> finalisation from tp_dealloc(). Instead, both the finalisation and the
> deallocation should be called externally and independently from the cleanup
> mechanism behind Py_DECREF(). (There is no problem in CS that can't be
> solved by adding another level of indirection...)
Why not, but I'm not sure that would solve anything on your side. If it
does, would you like to cook a patch? I wonder if there's some
unexpected issue with doing what you're proposing.
> An obvious open question is how to deal with exceptions during
> finalisation. Any break in the execution chain would mean that a part of
> the type wouldn't be finalised.
Let's come back to pure Python:
class A:
def __del__(self):
1/0
class B(A):
def __del__(self):
super().__del__()
self.cleanup_resources()
If you want cleanup_resources() to be called always (despite
A.__del__() raising), you have to use a try/finally block. There's no
magic here.
Letting the user call the upper finalizer explicitly lets them choose
their preferred form of exception handling.
Regards
Antoine.
More information about the Python-Dev
mailing list