[Python-Dev] PEP 442 clarification for type hierarchies

Antoine Pitrou solipsis at pitrou.net
Tue Aug 6 14:12:03 CEST 2013


Le Mon, 05 Aug 2013 22:30:29 +0200,
Stefan Behnel <stefan_ml at behnel.de> a écrit :
> 
> Hmm, it's a bit unfortunate that tp_finalize() maps so directly to
> __del__(), but I think this can be fixed. In any case, each
> tp_finalize() function must only ever be called once, so if a subtype
> inherited the tp_finalize() slot from its parent, it mustn't be
> called again.

This is already dealt with by a custom bit in the GC header (cf.
_PyGC_IS_FINALIZED, IIRC).

> >> 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()
> 
> What makes you think it's a good idea to call the parent type's
> finaliser before doing the local finalisation, and not the other way
> round? What if the subtype needs access to parts of the super type
> for its cleanup?

I'm not saying it's a good idea. I'm just saying that to reason about
the C API, it is a good idea to reason about equivalent pure Python
code. Since exceptions aren't implicitly silenced in pure Python code,
they probably shouldn't in C code.

> In other words, which makes more sense (at the C level):
> 
>     try:
>         super().tp_finalize()
>     finally:
>         local_cleanup()
> 
> or
> 
>     try:
>         local_cleanup()
>     finally:
>         super().tp_finalize()
> 
> Should that order be part of the protocol or not? (well, not for
> __del__() I guess, but maybe for tp_finalize()?)

No, it is left to the user's preference. Since tp_finalize() is meant
to be equivalent to __del__(), I think it's better if the protocols
aren't subtly different (to the extent to which it is possible, of
course).

> Coming back to the __del__() vs. tp_finalize() story, if tp_finalize()
> first recursed into the super types, the top-most one of which then
> calls __del__() and returns, we'd get an execution order that runs
> Python-level __del__() methods before C-level tp_finalize()
> functions, but loose the subtype-before-supertype execution order for
> tp_finalize() functions.

Well... to get that, you'd have to subclass a pure Python class with a
C extension type. Does that ever happen?

> That might call for a three-step cleanup:
> 
> 1) run all Python __del__() methods recursively
> 2) run all tp_finalize() functions recursively
> 3) run tp_dealloc() recursively

I don't see any reason why tp_finalize should be distinct from __del__,
while e.g. __init__ and tp_init map to the exact same thing.

(you might wonder why tp_finalize isn't called tp_del, but that's
because there is already something named tp_del - something which is
obsoleted by PEP 442, incidently ;-)).

Regards

Antoine.




More information about the Python-Dev mailing list