diff options
author | Richard Oudkerk <shibturn@gmail.com> | 2013-05-05 20:59:04 +0100 |
---|---|---|
committer | Richard Oudkerk <shibturn@gmail.com> | 2013-05-05 20:59:04 +0100 |
commit | 1a37adbbd1283f626e5e0ece422bbd07db3af520 (patch) | |
tree | 820633f56f5d8932fd4fbb0b6effdba1cec14f06 /Doc | |
parent | 839cafad81d66b9a8cad3021d06ad9b394512bbd (diff) | |
download | cpython-1a37adbbd1283f626e5e0ece422bbd07db3af520.tar.gz |
Issue #15528: Add weakref.finalize to support finalization using
weakref callbacks.
Diffstat (limited to 'Doc')
-rw-r--r-- | Doc/library/weakref.rst | 215 |
1 files changed, 210 insertions, 5 deletions
diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst index ec50107c75..5b5e460156 100644 --- a/Doc/library/weakref.rst +++ b/Doc/library/weakref.rst @@ -51,10 +51,15 @@ garbage collection. :class:`WeakSet` implements the :class:`set` interface, but keeps weak references to its elements, just like a :class:`WeakKeyDictionary` does. -Most programs should find that using one of these weak container types is all -they need -- it's not usually necessary to create your own weak references -directly. The low-level machinery used by the weak dictionary implementations -is exposed by the :mod:`weakref` module for the benefit of advanced uses. +:class:`finalize` provides a straight forward way to register a +cleanup function to be called when an object is garbage collected. +This is simpler to use than setting up a callback function on a raw +weak reference. + +Most programs should find that using one of these weak container types +or :class:`finalize` is all they need -- it's not usually necessary to +create your own weak references directly. The low-level machinery is +exposed by the :mod:`weakref` module for the benefit of advanced uses. Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, sets, @@ -117,7 +122,16 @@ Extension types can easily be made to support weak references; see weakref. If there is no callback or if the referent of the weakref is no longer alive then this attribute will have value ``None``. - .. versionadded:: 3.4 + .. note:: + + Like :meth:`__del__` methods, weak reference callbacks can be + called during interpreter shutdown when module globals have been + overwritten with :const:`None`. This can make writing robust + weak reference callbacks a challenge. Callbacks registered + using :class:`finalize` do not have to worry about this issue + because they will not be run after module teardown has begun. + + .. versionchanged:: 3.4 Added the :attr:`__callback__` attribute. @@ -229,6 +243,66 @@ These method have the same issues as the and :meth:`keyrefs` method of .. versionadded:: 3.4 +.. class:: finalize(obj, func, *args, **kwargs) + + Return a callable finalizer object which will be called when *obj* + is garbage collected. A finalizer is *alive* until it is called + (either explicitly or at garbage collection), and after that it is + *dead*. Calling a live finalizer returns the result of evaluating + ``func(*arg, **kwargs)``, whereas calling a dead finalizer returns + :const:`None`. + + Exceptions raised by finalizer callbacks during garbage collection + will be shown on the standard error output, but cannot be + propagated. They are handled in the same way as exceptions raised + from an object's :meth:`__del__` method or a weak reference's + callback. + + When the program exits, each remaining live finalizer is called + unless its :attr:`atexit` attribute has been set to false. They + are called in reverse order of creation. + + A finalizer will never invoke its callback during the later part of + the interpreter shutdown when module globals are liable to have + been replaced by :const:`None`. + + .. method:: __call__() + + If *self* is alive then mark it as dead and return the result of + calling ``func(*args, **kwargs)``. If *self* is dead then return + :const:`None`. + + .. method:: detach() + + If *self* is alive then mark it as dead and return the tuple + ``(obj, func, args, kwargs)``. If *self* is dead then return + :const:`None`. + + .. method:: peek() + + If *self* is alive then return the tuple ``(obj, func, args, + kwargs)``. If *self* is dead then return :const:`None`. + + .. attribute:: alive + + Property which is true if the finalizer is alive, false otherwise. + + .. attribute:: atexit + + A writable boolean property which by default is true. When the + program exits, it calls all remaining live finalizers for which + :attr:`.atexit` is true. They are called in reverse order of + creation. + + .. 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*. + + .. versionadded:: 3.4 + .. data:: ReferenceType @@ -365,3 +439,134 @@ objects can still be retrieved by ID if they do. def id2obj(oid): return _id2obj_dict[oid] + +.. _finalize-examples: + +Finalizer Objects +----------------- + +Often one uses :class:`finalize` to register a callback without +bothering to keep the returned finalizer object. For instance + + >>> import weakref + >>> class Object: + ... pass + ... + >>> kenny = Object() + >>> weakref.finalize(kenny, print, "You killed Kenny!") #doctest:+ELLIPSIS + <finalize object at ...; for 'Object' at ...> + >>> del kenny + You killed Kenny! + +The finalizer can be called directly as well. However the finalizer +will invoke the callback at most once. + + >>> def callback(x, y, z): + ... print("CALLBACK") + ... return x + y + z + ... + >>> obj = Object() + >>> f = weakref.finalize(obj, callback, 1, 2, z=3) + >>> assert f.alive + >>> assert f() == 6 + CALLBACK + >>> assert not f.alive + >>> f() # callback not called because finalizer dead + >>> del obj # callback not called because finalizer dead + +You can unregister a finalizer using its :meth:`~finalize.detach` +method. This kills the finalizer and returns the arguments passed to +the constructor when it was created. + + >>> obj = Object() + >>> f = weakref.finalize(obj, callback, 1, 2, z=3) + >>> f.detach() #doctest:+ELLIPSIS + (<__main__.Object object ...>, <function callback ...>, (1, 2), {'z': 3}) + >>> newobj, func, args, kwargs = _ + >>> assert not f.alive + >>> assert newobj is obj + >>> assert func(*args, **kwargs) == 6 + CALLBACK + +Unless you set the :attr:`~finalize.atexit` attribute to +:const:`False`, a finalizer will be called when the program exit if it +is still alive. For instance + + >>> obj = Object() + >>> weakref.finalize(obj, print, "obj dead or exiting") #doctest:+ELLIPSIS + <finalize object at ...; for 'Object' at ...> + >>> exit() #doctest:+SKIP + obj dead or exiting + + +Comparing finalizers with :meth:`__del__` methods +------------------------------------------------- + +Suppose we want to create a class whose instances represent temporary +directories. The directories should be deleted with their contents +when the first of the following events occurs: + +* the object is garbage collected, +* the object's :meth:`remove` method is called, or +* the program exits. + +We might try to implement the class using a :meth:`__del__` method as +follows:: + + class TempDir: + def __init__(self): + self.name = tempfile.mkdtemp() + + def remove(self): + if self.name is not None: + shutil.rmtree(self.name) + self.name = None + + @property + def removed(self): + return self.name is None + + def __del__(self): + self.remove() + +This solution has a couple of serious problems: + +* There is no guarantee that the object will be garbage collected + before the program exists, so the directory might be left. This is + because reference cycles containing an object with a :meth:`__del__` + method can never be collected. And even if the :class:`TempDir` + object is not itself part of a reference cycle, it may still be kept + alive by some unkown uncollectable reference cycle. + +* The :meth:`__del__` method may be called at shutdown after the + :mod:`shutil` module has been cleaned up, in which case + :attr:`shutil.rmtree` will have been replaced by :const:`None`. + This will cause the :meth:`__del__` method to fail and the directory + will not be removed. + +Using finalizers we can avoid these problems:: + + class TempDir: + def __init__(self): + self.name = tempfile.mkdtemp() + self._finalizer = weakref.finalize(self, shutil.rmtree, self.name) + + def remove(self): + self._finalizer() + + @property + def removed(self): + return not self._finalizer.alive + +Defined like this, even if a :class:`TempDir` object is part of a +reference cycle, that reference cycle can still be garbage collected. +If the object never gets garbage collected the finalizer will still be +called at exit. + +.. note:: + + If you create a finalizer object in a daemonic thread just as the + the program exits then there is the possibility that the finalizer + does not get called at exit. However, in a daemonic thread + :func:`atexit.register`, ``try: ... finally: ...`` and ``with: ...`` + do not guarantee that cleanup occurs either. |