[Python-Dev] PEP 442 clarification for type hierarchies (original) (raw)

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 tpdealloc 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 tpdealloc(). Instead, both the finalisation and the deallocation should be called externally and independently from the cleanup mechanism behind PyDECREF(). (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