[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