]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/blinker/_saferef.py
1 # extracted from Louie, http://pylouie.org/
4 # Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher,
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above
15 # copyright notice, this list of conditions and the following
16 # disclaimer in the documentation and/or other materials provided
17 # with the distribution.
19 # * Neither the name of the <ORGANIZATION> nor the names of its
20 # contributors may be used to endorse or promote products derived
21 # from this software without specific prior written permission.
23 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 """Refactored 'safe reference from dispatcher.py"""
42 get_self
= operator
.attrgetter("__self__")
43 get_func
= operator
.attrgetter("__func__")
46 def safe_ref(target
, on_delete
=None):
47 """Return a *safe* weak reference to a callable target.
49 - ``target``: The object to be weakly referenced, if it's a bound
50 method reference, will create a BoundMethodWeakref, otherwise
51 creates a simple weakref.
53 - ``on_delete``: If provided, will have a hard reference stored to
54 the callable to be called after the safe reference goes out of
55 scope with the reference object, (either a weakref or a
56 BoundMethodWeakref) as argument.
59 im_self
= get_self(target
)
60 except AttributeError:
61 if callable(on_delete
):
62 return weakref
.ref(target
, on_delete
)
64 return weakref
.ref(target
)
66 if im_self
is not None:
67 # Turn a bound method into a BoundMethodWeakref instance.
68 # Keep track of these instances for lookup by disconnect().
69 assert hasattr(target
, "im_func") or hasattr(target
, "__func__"), (
70 f
"safe_ref target {target!r} has im_self, but no im_func, "
71 "don't know how to create reference"
73 reference
= BoundMethodWeakref(target
=target
, on_delete
=on_delete
)
77 class BoundMethodWeakref
:
78 """'Safe' and reusable weak references to instance methods.
80 BoundMethodWeakref objects provide a mechanism for referencing a
81 bound method without requiring that the method object itself
82 (which is normally a transient object) is kept alive. Instead,
83 the BoundMethodWeakref object keeps weak references to both the
84 object and the function which together define the instance method.
88 - ``key``: The identity key for the reference, calculated by the
89 class's calculate_key method applied to the target instance method.
91 - ``deletion_methods``: Sequence of callable objects taking single
92 argument, a reference to this object which will be called when
93 *either* the target object or target function is garbage
94 collected (i.e. when this object becomes invalid). These are
95 specified as the on_delete parameters of safe_ref calls.
97 - ``weak_self``: Weak reference to the target object.
99 - ``weak_func``: Weak reference to the target function.
103 - ``_all_instances``: Class attribute pointing to all live
104 BoundMethodWeakref objects indexed by the class's
105 calculate_key(target) method applied to the target objects.
106 This weak value dictionary is used to short-circuit creation so
107 that multiple references to the same (object, function) pair
108 produce the same BoundMethodWeakref instance.
111 _all_instances
= weakref
.WeakValueDictionary() # type: ignore[var-annotated]
113 def __new__(cls
, target
, on_delete
=None, *arguments
, **named
):
114 """Create new instance or return current instance.
116 Basically this method of construction allows us to
117 short-circuit creation of references to already-referenced
118 instance methods. The key corresponding to the target is
119 calculated, and if there is already an existing reference,
120 that is returned, with its deletion_methods attribute updated.
121 Otherwise the new instance is created and registered in the
122 table of already-referenced methods.
124 key
= cls
.calculate_key(target
)
125 current
= cls
._all
_instances
.get(key
)
126 if current
is not None:
127 current
.deletion_methods
.append(on_delete
)
130 base
= super().__new
__(cls
)
131 cls
._all
_instances
[key
] = base
132 base
.__init
__(target
, on_delete
, *arguments
, **named
)
135 def __init__(self
, target
, on_delete
=None):
136 """Return a weak-reference-like instance for a bound method.
138 - ``target``: The instance-method target for the weak reference,
139 must have im_self and im_func attributes and be
140 reconstructable via the following, which is true of built-in
143 target.im_func.__get__( target.im_self )
145 - ``on_delete``: Optional callback which will be called when
146 this weak reference ceases to be valid (i.e. either the
147 object or the function is garbage collected). Should take a
148 single argument, which will be passed a pointer to this
152 def remove(weak
, self
=self
):
153 """Set self.isDead to True when method or instance is destroyed."""
154 methods
= self
.deletion_methods
[:]
155 del self
.deletion_methods
[:]
157 del self
.__class
__._all
_instances
[self
.key
]
160 for function
in methods
:
162 if callable(function
):
166 traceback
.print_exc()
167 except AttributeError:
168 e
= sys
.exc_info()[1]
170 f
"Exception during saferef {self} "
171 f
"cleanup function {function}: {e}"
174 self
.deletion_methods
= [on_delete
]
175 self
.key
= self
.calculate_key(target
)
176 im_self
= get_self(target
)
177 im_func
= get_func(target
)
178 self
.weak_self
= weakref
.ref(im_self
, remove
)
179 self
.weak_func
= weakref
.ref(im_func
, remove
)
180 self
.self_name
= str(im_self
)
181 self
.func_name
= str(im_func
.__name
__)
184 def calculate_key(cls
, target
):
185 """Calculate the reference key for this reference.
187 Currently this is a two-tuple of the id()'s of the target
188 object and the target function respectively.
190 return (id(get_self(target
)), id(get_func(target
)))
193 """Give a friendly representation of the object."""
194 return "{}({}.{})".format(
195 self
.__class
__.__name
__,
203 return hash((self
.self_name
, self
.key
))
205 def __nonzero__(self
):
206 """Whether we are still a valid reference."""
207 return self() is not None
209 def __eq__(self
, other
):
210 """Compare with another reference."""
211 if not isinstance(other
, self
.__class
__):
212 return operator
.eq(self
.__class
__, type(other
))
213 return operator
.eq(self
.key
, other
.key
)
216 """Return a strong reference to the bound method.
218 If the target cannot be retrieved, then will return None,
219 otherwise returns a bound instance method for our object and
222 Note: You may call this method any number of times, as it does
223 not invalidate the reference.
225 target
= self
.weak_self()
226 if target
is not None:
227 function
= self
.weak_func()
228 if function
is not None:
229 return function
.__get
__(target
)