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.

classification
Title: weakref.finalize documentation contradicts itself RE: finalizer callback or args referencing object
Type: Stage: resolved
Components: Documentation Versions: Python 3.10, Python 3.9, Python 3.8, Python 3.7, Python 3.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, ryan.a.heisler, tim.peters
Priority: normal Keywords:

Created on 2021-01-17 04:12 by ryan.a.heisler, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (3)
msg385155 - (view) Author: Ryan Heisler (ryan.a.heisler) Date: 2021-01-17 04:12
In the documentation for `weakref.finalize` (https://docs.python.org/3.9/library/weakref.html#weakref.finalize), it says:

"Note It is important to ensure that func, args and kwargs do not own any references to obj, either directly or indirectly, since otherwise obj will never be garbage collected. In particular, func should not be a bound method of obj."

However, at the bottom of the document, in the section called "Comparing finalizers with __del__() methods" (https://docs.python.org/3.8/library/weakref.html#comparing-finalizers-with-del-methods), the following code is part of an example of how to use `weakref.finalize`:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

I believe this code violates the rule that func, args, and kwargs should not have a reference to obj. In the example, obj is the instance of TempDir, and one of the arguments to finalize's callback is an attribute of the same instance of TempDir.

I do not know how to fix this example code. I found it while trying to figure out how to use `weakref.finalize`.
msg385156 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2021-01-17 04:40
Not a problem. Arguments to a function are evaluated before the function is invoked.  So in

self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

self.name is evaluated before weakref.finalize is called(). `self.name` _extracts_ the `.name` attribute of `self`, and the result is simply a pathname (returned by the earlier call to `mkdtemp()`), from which `self` cannot be accessed.

Just like, e.g., for just about any old object O, after

    O.name = 42

a later `O.name` extracts the int 42, with no trace of that 42 had anything to do with `O`.

This isn't so when for a bound method object: `O.method_name` constructs and returns an object that _does_ reference `O`. That's why the earlier docs highlight bound method objects. For an attribute `name` bound to a chunk of data, `O.name` just retrieves that data, which _generally_ has nothing to do with `O` beyond that it happens to be reachable from `O` (but also generally NOT the reverse).
msg385157 - (view) Author: Ryan Heisler (ryan.a.heisler) Date: 2021-01-17 05:08
Perfect, thanks for your quick response. I was passing a bound method of obj as the func to `weakref.finalize(obj, func, /, *args, **kwargs)`. It slipped my mind that an instance variable like self.name and a bound method like self.clean_up, though they both belong to self, would be evaluated differently.

For anyone wondering, I was looking for weakref.proxy (https://docs.python.org/3/library/weakref.html#weakref.proxy) or weakref.WeakMethod (https://docs.python.org/3/library/weakref.html#weakref.WeakMethod)
History
Date User Action Args
2022-04-11 14:59:40adminsetgithub: 87111
2021-01-17 05:08:41ryan.a.heislersetstatus: open -> closed
resolution: not a bug
messages: + msg385157

stage: resolved
2021-01-17 04:40:50tim.peterssetnosy: + tim.peters
messages: + msg385156
2021-01-17 04:12:18ryan.a.heislercreate