Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7a3dae0

Browse files
committed
Issue #15528: Add weakref.finalize to support finalization using
weakref callbacks. This is 2e446e87ac5b except that collections/__init__.py has been modified to import proxy from _weakref instead of weakref. This eliminates an import cycle which seems to cause a problem on Unix but not Windows.
1 parent 8408cea commit 7a3dae0

5 files changed

Lines changed: 501 additions & 8 deletions

File tree

Doc/library/weakref.rst

Lines changed: 210 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,15 @@ garbage collection. :class:`WeakSet` implements the :class:`set` interface,
5151
but keeps weak references to its elements, just like a
5252
:class:`WeakKeyDictionary` does.
5353

54-
Most programs should find that using one of these weak container types is all
55-
they need -- it's not usually necessary to create your own weak references
56-
directly. The low-level machinery used by the weak dictionary implementations
57-
is exposed by the :mod:`weakref` module for the benefit of advanced uses.
54+
:class:`finalize` provides a straight forward way to register a
55+
cleanup function to be called when an object is garbage collected.
56+
This is simpler to use than setting up a callback function on a raw
57+
weak reference.
58+
59+
Most programs should find that using one of these weak container types
60+
or :class:`finalize` is all they need -- it's not usually necessary to
61+
create your own weak references directly. The low-level machinery is
62+
exposed by the :mod:`weakref` module for the benefit of advanced uses.
5863

5964
Not all objects can be weakly referenced; those objects which can include class
6065
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
117122
weakref. If there is no callback or if the referent of the weakref is
118123
no longer alive then this attribute will have value ``None``.
119124

120-
.. versionadded:: 3.4
125+
.. note::
126+
127+
Like :meth:`__del__` methods, weak reference callbacks can be
128+
called during interpreter shutdown when module globals have been
129+
overwritten with :const:`None`. This can make writing robust
130+
weak reference callbacks a challenge. Callbacks registered
131+
using :class:`finalize` do not have to worry about this issue
132+
because they will not be run after module teardown has begun.
133+
134+
.. versionchanged:: 3.4
121135
Added the :attr:`__callback__` attribute.
122136

123137

@@ -229,6 +243,66 @@ These method have the same issues as the and :meth:`keyrefs` method of
229243

230244
.. versionadded:: 3.4
231245

246+
.. class:: finalize(obj, func, *args, **kwargs)
247+
248+
Return a callable finalizer object which will be called when *obj*
249+
is garbage collected. A finalizer is *alive* until it is called
250+
(either explicitly or at garbage collection), and after that it is
251+
*dead*. Calling a live finalizer returns the result of evaluating
252+
``func(*arg, **kwargs)``, whereas calling a dead finalizer returns
253+
:const:`None`.
254+
255+
Exceptions raised by finalizer callbacks during garbage collection
256+
will be shown on the standard error output, but cannot be
257+
propagated. They are handled in the same way as exceptions raised
258+
from an object's :meth:`__del__` method or a weak reference's
259+
callback.
260+
261+
When the program exits, each remaining live finalizer is called
262+
unless its :attr:`atexit` attribute has been set to false. They
263+
are called in reverse order of creation.
264+
265+
A finalizer will never invoke its callback during the later part of
266+
the interpreter shutdown when module globals are liable to have
267+
been replaced by :const:`None`.
268+
269+
.. method:: __call__()
270+
271+
If *self* is alive then mark it as dead and return the result of
272+
calling ``func(*args, **kwargs)``. If *self* is dead then return
273+
:const:`None`.
274+
275+
.. method:: detach()
276+
277+
If *self* is alive then mark it as dead and return the tuple
278+
``(obj, func, args, kwargs)``. If *self* is dead then return
279+
:const:`None`.
280+
281+
.. method:: peek()
282+
283+
If *self* is alive then return the tuple ``(obj, func, args,
284+
kwargs)``. If *self* is dead then return :const:`None`.
285+
286+
.. attribute:: alive
287+
288+
Property which is true if the finalizer is alive, false otherwise.
289+
290+
.. attribute:: atexit
291+
292+
A writable boolean property which by default is true. When the
293+
program exits, it calls all remaining live finalizers for which
294+
:attr:`.atexit` is true. They are called in reverse order of
295+
creation.
296+
297+
.. note::
298+
299+
It is important to ensure that *func*, *args* and *kwargs* do
300+
not own any references to *obj*, either directly or indirectly,
301+
since otherwise *obj* will never be garbage collected. In
302+
particular, *func* should not be a bound method of *obj*.
303+
304+
.. versionadded:: 3.4
305+
232306

233307
.. data:: ReferenceType
234308

@@ -365,3 +439,134 @@ objects can still be retrieved by ID if they do.
365439
def id2obj(oid):
366440
return _id2obj_dict[oid]
367441

442+
443+
.. _finalize-examples:
444+
445+
Finalizer Objects
446+
-----------------
447+
448+
Often one uses :class:`finalize` to register a callback without
449+
bothering to keep the returned finalizer object. For instance
450+
451+
>>> import weakref
452+
>>> class Object:
453+
... pass
454+
...
455+
>>> kenny = Object()
456+
>>> weakref.finalize(kenny, print, "You killed Kenny!") #doctest:+ELLIPSIS
457+
<finalize object at ...; for 'Object' at ...>
458+
>>> del kenny
459+
You killed Kenny!
460+
461+
The finalizer can be called directly as well. However the finalizer
462+
will invoke the callback at most once.
463+
464+
>>> def callback(x, y, z):
465+
... print("CALLBACK")
466+
... return x + y + z
467+
...
468+
>>> obj = Object()
469+
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
470+
>>> assert f.alive
471+
>>> assert f() == 6
472+
CALLBACK
473+
>>> assert not f.alive
474+
>>> f() # callback not called because finalizer dead
475+
>>> del obj # callback not called because finalizer dead
476+
477+
You can unregister a finalizer using its :meth:`~finalize.detach`
478+
method. This kills the finalizer and returns the arguments passed to
479+
the constructor when it was created.
480+
481+
>>> obj = Object()
482+
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
483+
>>> f.detach() #doctest:+ELLIPSIS
484+
(<__main__.Object object ...>, <function callback ...>, (1, 2), {'z': 3})
485+
>>> newobj, func, args, kwargs = _
486+
>>> assert not f.alive
487+
>>> assert newobj is obj
488+
>>> assert func(*args, **kwargs) == 6
489+
CALLBACK
490+
491+
Unless you set the :attr:`~finalize.atexit` attribute to
492+
:const:`False`, a finalizer will be called when the program exit if it
493+
is still alive. For instance
494+
495+
>>> obj = Object()
496+
>>> weakref.finalize(obj, print, "obj dead or exiting") #doctest:+ELLIPSIS
497+
<finalize object at ...; for 'Object' at ...>
498+
>>> exit() #doctest:+SKIP
499+
obj dead or exiting
500+
501+
502+
Comparing finalizers with :meth:`__del__` methods
503+
-------------------------------------------------
504+
505+
Suppose we want to create a class whose instances represent temporary
506+
directories. The directories should be deleted with their contents
507+
when the first of the following events occurs:
508+
509+
* the object is garbage collected,
510+
* the object's :meth:`remove` method is called, or
511+
* the program exits.
512+
513+
We might try to implement the class using a :meth:`__del__` method as
514+
follows::
515+
516+
class TempDir:
517+
def __init__(self):
518+
self.name = tempfile.mkdtemp()
519+
520+
def remove(self):
521+
if self.name is not None:
522+
shutil.rmtree(self.name)
523+
self.name = None
524+
525+
@property
526+
def removed(self):
527+
return self.name is None
528+
529+
def __del__(self):
530+
self.remove()
531+
532+
This solution has a couple of serious problems:
533+
534+
* There is no guarantee that the object will be garbage collected
535+
before the program exists, so the directory might be left. This is
536+
because reference cycles containing an object with a :meth:`__del__`
537+
method can never be collected. And even if the :class:`TempDir`
538+
object is not itself part of a reference cycle, it may still be kept
539+
alive by some unkown uncollectable reference cycle.
540+
541+
* The :meth:`__del__` method may be called at shutdown after the
542+
:mod:`shutil` module has been cleaned up, in which case
543+
:attr:`shutil.rmtree` will have been replaced by :const:`None`.
544+
This will cause the :meth:`__del__` method to fail and the directory
545+
will not be removed.
546+
547+
Using finalizers we can avoid these problems::
548+
549+
class TempDir:
550+
def __init__(self):
551+
self.name = tempfile.mkdtemp()
552+
self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)
553+
554+
def remove(self):
555+
self._finalizer()
556+
557+
@property
558+
def removed(self):
559+
return not self._finalizer.alive
560+
561+
Defined like this, even if a :class:`TempDir` object is part of a
562+
reference cycle, that reference cycle can still be garbage collected.
563+
If the object never gets garbage collected the finalizer will still be
564+
called at exit.
565+
566+
.. note::
567+
568+
If you create a finalizer object in a daemonic thread just as the
569+
the program exits then there is the possibility that the finalizer
570+
does not get called at exit. However, in a daemonic thread
571+
:func:`atexit.register`, ``try: ... finally: ...`` and ``with: ...``
572+
do not guarantee that cleanup occurs either.

Lib/collections/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from keyword import iskeyword as _iskeyword
1313
import sys as _sys
1414
import heapq as _heapq
15-
from weakref import proxy as _proxy
15+
from _weakref import proxy as _proxy
1616
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
1717
from reprlib import recursive_repr as _recursive_repr
1818

0 commit comments

Comments
 (0)