This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author tim.peters
Recipients konrad.schwarz, mark.dickinson, pitrou, tim.peters
Date 2021-03-03.21:07:23
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1614805643.91.0.893190678267.issue43383@roundup.psfhosted.org>
In-reply-to
Content
This won't go anywhere without code (preferably minimal) we can run to reproduce the complaint. If there were a "general principle" at work here, someone else would surely have reported it over the last few decades ;-)

To the contrary, the common confusion is in the _other_ direction: a weakref callback _not_ getting invoked when a programmer thinks it "should be". The cause for that is always the same: the weakref object died before the weakly referenced object died. That was the primary motivation for introducing `weakref.finalize()`.

CPython does not, in general, "batch" (or in any other way delay) object destruction. An object is destroyed - immediately - when its refcount falls to 0. In a technical sense, that's also true in the case of cycles (in which case the gc module artificially drives refcounts to 0, based on liveness and reachability analysis). With very few exceptions, neither does it hang on to "hidden" references. The primary exception to that is in interactive shells, where the module-global identifier "_" is typically bound, by magic, to the object most recently displayed. In the case of exceptions, it's also possible for programs to accidentally hang on to the exception object, from which the entire chain of stack frames back to the source of the exception can be reached.

So, based on what you said, this is the best attempt I can make:

    import sys, weakref

    class C:
        def __del__(self):
            print("__del__ called")

    def wrcb(x):
        print("weakref callback called")

    c = C()
    d = {"x" : weakref.ref(c, wrcb)}
    print(sys.getrefcount(d["x"]))

    #del d["x"]
    del c

which displays:

    2
    __del__ called
    weakref callback called

Note the 2! If the moral equivalent in your code displays a number larger than 2, then there are more references to the weakref object than just as a dict value (that's one; the other reference comes from temporarily incrementing the refcount of `d["x"]` to pass it as an argument to `getrefcount()`).

If I uncomment the penultimate line, to destroy the weakref before `c` is destroyed, the output changes to:

    2
    __del__ called

So it's all as expected. Can you change that code to demonstrate your case?
History
Date User Action Args
2021-03-03 21:07:23tim.peterssetrecipients: + tim.peters, mark.dickinson, pitrou, konrad.schwarz
2021-03-03 21:07:23tim.peterssetmessageid: <1614805643.91.0.893190678267.issue43383@roundup.psfhosted.org>
2021-03-03 21:07:23tim.peterslinkissue43383 messages
2021-03-03 21:07:23tim.peterscreate