Could there be a clearer separation between the language semantics and current CPython behavior?

On 2020-01-02 03:53, Yonatan Zunger wrote:
Oh, I'm absolutely thinking about clarity. Something like:

----

This method is called when the instance is about to be destroyed. Because it may be called by either the ordinary Python execution flow or by the garbage collector, it has very unusual semantics, and must be treated with great care.


The precise semantics of when __del__ is called on an object are implementation-dependent. For example: * It might be invoked during the normal interpreter flow at a moment like function return, if the reference count for an object has dropped to zero; * It might be invoked by the garbage collector, which happens during the normal interpreter flow but is called from an arbitrary thread; * It might be invoked during interpreter shutdown, after various global variables (including modules!) have already been deleted, or other threads' interpreters have already stopped. * It may not be invoked at all; it is /not guaranteed/ that __del__() methods are called for objects that still exist when the interpreter exits.

Note that similar caveats apply for alternatives to __del__: weakref.finalize and atexit.register.

Doing any of the following in an implementation of __del__() may easily lead to bugs, nondeterministic behavior, and inconsistent behavior across different implementations or versions of Python:

* Assuming that any variables outside self exist, including modules or global variables; * Attempting to acquire locks or otherwise block, since they may (e.g.) be invoked inside a thread which already holds those resources; * Relying on being able to signal other threads and wait for their responses (even thread joins), since those threads' interpreters may have exited; * Causing failures (e.g. leaving external devices in an invalid state) if __del__ is /never/ invoked.

In particular, del x does not call x.__del__() directly: the call may be delayed or not happen at all.

In CPython, del x decrements the reference count for x by one, and x.__del__() is only called either when x’s reference count reaches zero, or by the cyclic garbage collector at an unspecified time (e.g. if a reference cycle prevents the reference count from going to zero).

If a base class has a __del__() method, the derived class’s __del__() method, if any, must explicitly call it to ensure proper deletion of the base class part of the instance.

It is possible (though not recommended!) for the __del__() method to postpone destruction of the instance by creating a new reference to it. This is called object resurrection. It is implementation-dependent whether __del__() is called a second time when a resurrected object is about to be destroyed.
The current CPython implementation usually calls __del__ only once for each object, but this is not guaranteed in all cases.


On Wed, Jan 1, 2020 at 12:57 PM Andrew Svetlov <andrew.svet...@gmail.com <mailto:andrew.svet...@gmail.com>> wrote:

    If the warning text tells about the delayed execution -- I'm +1.
    -1 for something like "just don't use __del__, it is dangerous".

    On Wed, Jan 1, 2020, 21:51 Gregory P. Smith <g...@krypto.org
    <mailto:g...@krypto.org>> wrote:



        On Wed, Jan 1, 2020 at 6:40 AM Andrew Svetlov
        <andrew.svet...@gmail.com <mailto:andrew.svet...@gmail.com>> wrote:

            __del__ is very useful not for interpreter shutdown but as a
            regular
            destructor in object's lifecycle.


        The reason we should warn people against ever implementing
        __del__ is that people rarely actually understand object
        lifecycle.  Its presence guarantees delayed garbage collection
        and that its code will code execute in a context way outside of
        normal control flow that all other methods are invoked from.

        -gps


            Action on the shutdown is another beast.
            Personally, I prefer to do all finalization works by
            explicit calls instead.

            On Wed, Jan 1, 2020 at 2:39 AM Yonatan Zunger
            <zun...@humu.com <mailto:zun...@humu.com>> wrote:
             >
             > Hey everyone,
             >
             > I just encountered yet another reason to beware of
            __del__: when it's called during interpreter shutdown, for
            reasons which are kind of obvious in retrospect, if it calls
            notify() on a threading.Condition, the waiting thread may or
            may not ever actually receive it, and so if it does that and
            then tries to join() the thread the interpreter may hang in
            a hard-to-debug way.
             >
             > This isn't something that can reasonably be fixed, and
            (like in most cases involving __del__) there's a very simple
            fix of using weakref.finalize instead. My question for the
            dev list: How would people feel about changing the
            documentation for the method to more bluntly warn people
            against using it, and refer them to weakref.finalize and/or
            atexit.register as an alternative? The text already has an
            undertone of "lasciate ogni speranza, voi ch'entrate" but it
            may be helpful to be more explicit to avoid people getting
            caught in unexpected pitfalls.
             >
             > Yonatan
             > _______________________________________________
             > Python-Dev mailing list -- python-dev@python.org
            <mailto:python-dev@python.org>
             > To unsubscribe send an email to
            python-dev-le...@python.org <mailto:python-dev-le...@python.org>
             > https://mail.python.org/mailman3/lists/python-dev.python.org/
             > Message archived at
            
https://mail.python.org/archives/list/python-dev@python.org/message/AAZQBWD6PHC4PVNCCPX4A2745SS7B3LS/
             > Code of Conduct: http://python.org/psf/codeofconduct/



-- Thanks,
            Andrew Svetlov
            _______________________________________________
            Python-Dev mailing list -- python-dev@python.org
            <mailto:python-dev@python.org>
            To unsubscribe send an email to python-dev-le...@python.org
            <mailto:python-dev-le...@python.org>
            https://mail.python.org/mailman3/lists/python-dev.python.org/
            Message archived at
            
https://mail.python.org/archives/list/python-dev@python.org/message/57CDW4NIYEQ3JEVX2JVCJDA5TXTC5MBR/
            Code of Conduct: http://python.org/psf/codeofconduct/


_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/NKWOXME256DT53PRLU3IMWORT2DZTRHO/
Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/7OKNWI32HPTFRKJUZFL5EKSV4MDU4Q5D/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to