Let us walk on the 3-isogeny graph
Loading...
Searching...
No Matches
weakref_finalize.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2"""
3backports.weakref_finalize
4~~~~~~~~~~~~~~~~~~
5
6Backports the Python 3 ``weakref.finalize`` method.
7"""
8from __future__ import absolute_import
9
10import itertools
11import sys
12from weakref import ref
13
14__all__ = ["weakref_finalize"]
15
16
17class 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.
27 """
28
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.
32
33 __slots__ = ()
34 _registry = {}
35 _shutdown = False
36 _index_iter = itertools.count()
37 _dirty = False
38 _registered_with_atexit = False
39
40 class _Info(object):
41 __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
42
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
47 import atexit
48
51 info = self._Info()
52 info.weakref = ref(obj, self)
53 info.func = func
54 info.args = args
55 info.kwargs = kwargs or None
56 info.atexit = True
57 info.index = next(self._index_iter)
58 self._registry[self] = info
60
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 {}))
67
68 def detach(self):
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 {})
75
76 def peek(self):
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()
81 if obj is not None:
82 return (obj, info.func, info.args, info.kwargs or {})
83
84 @property
85 def alive(self):
86 """Whether finalizer is alive"""
87 return self in self._registry
88
89 @property
90 def atexit(self):
91 """Whether finalizer should be called at exit"""
92 info = self._registry.get(self)
93 return bool(info) and info.atexit
94
95 @atexit.setter
96 def atexit(self, value):
97 info = self._registry.get(self)
98 if info:
99 info.atexit = bool(value)
100
101 def __repr__(self):
102 info = self._registry.get(self)
103 obj = info and info.weakref()
104 if obj is None:
105 return "<%s object at %#x; dead>" % (type(self).__name__, id(self))
106 else:
107 return "<%s object at %#x; for %r at %#x>" % (
108 type(self).__name__,
109 id(self),
110 type(obj).__name__,
111 id(obj),
112 )
113
114 @classmethod
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]
120
121 @classmethod
122 def _exitfunc(cls):
123 # At shutdown invoke finalizers for which atexit is true.
124 # This is called once all other non-daemonic threads have been
125 # joined.
126 reenable_gc = False
127 try:
128 if cls._registry:
129 import gc
130
131 if gc.isenabled():
132 reenable_gc = True
133 gc.disable()
134 pending = None
135 while True:
136 if pending is None or weakref_finalize._dirty:
137 pending = cls._select_for_exit()
139 if not pending:
140 break
141 f = pending.pop()
142 try:
143 # gc is disabled, so (assuming no daemonic
144 # threads) the following is the only line in
145 # this function which might trigger creation
146 # of a new finalizer
147 f()
148 except Exception:
150 assert f not in cls._registry
151 finally:
152 # prevent any more finalizers from executing during shutdown
154 if reenable_gc:
155 gc.enable()
for i