1 # -*- coding: utf-8 -*-
3 backports.weakref_finalize
6 Backports the Python 3 ``weakref.finalize`` method.
8 from __future__ import absolute_import
12 from weakref import ref
14 __all__ = ["weakref_finalize"]
17 class weakref_finalize(object):
18 """Class for finalization of weakrefable objects
19 finalize(obj, func, *args, **kwargs) returns a callable finalizer
20 object which will be called when obj is garbage collected. The
21 first time the finalizer is called it evaluates func(*arg, **kwargs)
22 and returns the result. After this the finalizer is dead, and
23 calling it just returns None.
24 When the program exits any remaining finalizers for which the
25 atexit attribute is true will be run in reverse order of creation.
26 By default atexit is true.
29 # Finalizer objects don't have any state of their own. They are
30 # just used as keys to lookup _Info objects in the registry. This
31 # ensures that they cannot be part of a ref-cycle.
36 _index_iter = itertools.count()
38 _registered_with_atexit = False
41 __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
43 def __init__(self, obj, func, *args, **kwargs):
44 if not self._registered_with_atexit:
45 # We may register the exit function more than once because
46 # of a thread race, but that is harmless
49 atexit.register(self._exitfunc)
50 weakref_finalize._registered_with_atexit = True
52 info.weakref = ref(obj, self)
55 info.kwargs = kwargs or None
57 info.index = next(self._index_iter)
58 self._registry[self] = info
59 weakref_finalize._dirty = True
61 def __call__(self, _=None):
62 """If alive then mark as dead and return func(*args, **kwargs);
63 otherwise return None"""
64 info = self._registry.pop(self, None)
65 if info and not self._shutdown:
66 return info.func(*info.args, **(info.kwargs or {}))
69 """If alive then mark as dead and return (obj, func, args, kwargs);
70 otherwise return None"""
71 info = self._registry.get(self)
72 obj = info and info.weakref()
73 if obj is not None and self._registry.pop(self, None):
74 return (obj, info.func, info.args, info.kwargs or {})
77 """If alive then return (obj, func, args, kwargs);
78 otherwise return None"""
79 info = self._registry.get(self)
80 obj = info and info.weakref()
82 return (obj, info.func, info.args, info.kwargs or {})
86 """Whether finalizer is alive"""
87 return self in self._registry
91 """Whether finalizer should be called at exit"""
92 info = self._registry.get(self)
93 return bool(info) and info.atexit
96 def atexit(self, value):
97 info = self._registry.get(self)
99 info.atexit = bool(value)
102 info = self._registry.get(self)
103 obj = info and info.weakref()
105 return "<%s object at %#x; dead>" % (type(self).__name__, id(self))
107 return "<%s object at %#x; for %r at %#x>" % (
115 def _select_for_exit(cls):
116 # Return live finalizers marked for exit, oldest first
117 L = [(f, i) for (f, i) in cls._registry.items() if i.atexit]
118 L.sort(key=lambda item: item[1].index)
119 return [f for (f, i) in L]
123 # At shutdown invoke finalizers for which atexit is true.
124 # This is called once all other non-daemonic threads have been
136 if pending is None or weakref_finalize._dirty:
137 pending = cls._select_for_exit()
138 weakref_finalize._dirty = False
143 # gc is disabled, so (assuming no daemonic
144 # threads) the following is the only line in
145 # this function which might trigger creation
149 sys.excepthook(*sys.exc_info())
150 assert f not in cls._registry
152 # prevent any more finalizers from executing during shutdown
153 weakref_finalize._shutdown = True