[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