Spaces:
Runtime error
Runtime error
| # extracted from Louie, http://pylouie.org/ | |
| # updated for Python 3 | |
| # | |
| # Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher, | |
| # Matthew R. Scott | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions are | |
| # met: | |
| # | |
| # * Redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer. | |
| # | |
| # * Redistributions in binary form must reproduce the above | |
| # copyright notice, this list of conditions and the following | |
| # disclaimer in the documentation and/or other materials provided | |
| # with the distribution. | |
| # | |
| # * Neither the name of the <ORGANIZATION> nor the names of its | |
| # contributors may be used to endorse or promote products derived | |
| # from this software without specific prior written permission. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| # | |
| """Refactored 'safe reference from dispatcher.py""" | |
| import operator | |
| import sys | |
| import traceback | |
| import weakref | |
| get_self = operator.attrgetter("__self__") | |
| get_func = operator.attrgetter("__func__") | |
| def safe_ref(target, on_delete=None): | |
| """Return a *safe* weak reference to a callable target. | |
| - ``target``: The object to be weakly referenced, if it's a bound | |
| method reference, will create a BoundMethodWeakref, otherwise | |
| creates a simple weakref. | |
| - ``on_delete``: If provided, will have a hard reference stored to | |
| the callable to be called after the safe reference goes out of | |
| scope with the reference object, (either a weakref or a | |
| BoundMethodWeakref) as argument. | |
| """ | |
| try: | |
| im_self = get_self(target) | |
| except AttributeError: | |
| if callable(on_delete): | |
| return weakref.ref(target, on_delete) | |
| else: | |
| return weakref.ref(target) | |
| else: | |
| if im_self is not None: | |
| # Turn a bound method into a BoundMethodWeakref instance. | |
| # Keep track of these instances for lookup by disconnect(). | |
| assert hasattr(target, "im_func") or hasattr(target, "__func__"), ( | |
| f"safe_ref target {target!r} has im_self, but no im_func, " | |
| "don't know how to create reference" | |
| ) | |
| reference = BoundMethodWeakref(target=target, on_delete=on_delete) | |
| return reference | |
| class BoundMethodWeakref: | |
| """'Safe' and reusable weak references to instance methods. | |
| BoundMethodWeakref objects provide a mechanism for referencing a | |
| bound method without requiring that the method object itself | |
| (which is normally a transient object) is kept alive. Instead, | |
| the BoundMethodWeakref object keeps weak references to both the | |
| object and the function which together define the instance method. | |
| Attributes: | |
| - ``key``: The identity key for the reference, calculated by the | |
| class's calculate_key method applied to the target instance method. | |
| - ``deletion_methods``: Sequence of callable objects taking single | |
| argument, a reference to this object which will be called when | |
| *either* the target object or target function is garbage | |
| collected (i.e. when this object becomes invalid). These are | |
| specified as the on_delete parameters of safe_ref calls. | |
| - ``weak_self``: Weak reference to the target object. | |
| - ``weak_func``: Weak reference to the target function. | |
| Class Attributes: | |
| - ``_all_instances``: Class attribute pointing to all live | |
| BoundMethodWeakref objects indexed by the class's | |
| calculate_key(target) method applied to the target objects. | |
| This weak value dictionary is used to short-circuit creation so | |
| that multiple references to the same (object, function) pair | |
| produce the same BoundMethodWeakref instance. | |
| """ | |
| _all_instances = weakref.WeakValueDictionary() # type: ignore[var-annotated] | |
| def __new__(cls, target, on_delete=None, *arguments, **named): | |
| """Create new instance or return current instance. | |
| Basically this method of construction allows us to | |
| short-circuit creation of references to already-referenced | |
| instance methods. The key corresponding to the target is | |
| calculated, and if there is already an existing reference, | |
| that is returned, with its deletion_methods attribute updated. | |
| Otherwise the new instance is created and registered in the | |
| table of already-referenced methods. | |
| """ | |
| key = cls.calculate_key(target) | |
| current = cls._all_instances.get(key) | |
| if current is not None: | |
| current.deletion_methods.append(on_delete) | |
| return current | |
| else: | |
| base = super().__new__(cls) | |
| cls._all_instances[key] = base | |
| base.__init__(target, on_delete, *arguments, **named) | |
| return base | |
| def __init__(self, target, on_delete=None): | |
| """Return a weak-reference-like instance for a bound method. | |
| - ``target``: The instance-method target for the weak reference, | |
| must have im_self and im_func attributes and be | |
| reconstructable via the following, which is true of built-in | |
| instance methods:: | |
| target.im_func.__get__( target.im_self ) | |
| - ``on_delete``: Optional callback which will be called when | |
| this weak reference ceases to be valid (i.e. either the | |
| object or the function is garbage collected). Should take a | |
| single argument, which will be passed a pointer to this | |
| object. | |
| """ | |
| def remove(weak, self=self): | |
| """Set self.isDead to True when method or instance is destroyed.""" | |
| methods = self.deletion_methods[:] | |
| del self.deletion_methods[:] | |
| try: | |
| del self.__class__._all_instances[self.key] | |
| except KeyError: | |
| pass | |
| for function in methods: | |
| try: | |
| if callable(function): | |
| function(self) | |
| except Exception: | |
| try: | |
| traceback.print_exc() | |
| except AttributeError: | |
| e = sys.exc_info()[1] | |
| print( | |
| f"Exception during saferef {self} " | |
| f"cleanup function {function}: {e}" | |
| ) | |
| self.deletion_methods = [on_delete] | |
| self.key = self.calculate_key(target) | |
| im_self = get_self(target) | |
| im_func = get_func(target) | |
| self.weak_self = weakref.ref(im_self, remove) | |
| self.weak_func = weakref.ref(im_func, remove) | |
| self.self_name = str(im_self) | |
| self.func_name = str(im_func.__name__) | |
| def calculate_key(cls, target): | |
| """Calculate the reference key for this reference. | |
| Currently this is a two-tuple of the id()'s of the target | |
| object and the target function respectively. | |
| """ | |
| return (id(get_self(target)), id(get_func(target))) | |
| def __str__(self): | |
| """Give a friendly representation of the object.""" | |
| return "{}({}.{})".format( | |
| self.__class__.__name__, | |
| self.self_name, | |
| self.func_name, | |
| ) | |
| __repr__ = __str__ | |
| def __hash__(self): | |
| return hash((self.self_name, self.key)) | |
| def __nonzero__(self): | |
| """Whether we are still a valid reference.""" | |
| return self() is not None | |
| def __eq__(self, other): | |
| """Compare with another reference.""" | |
| if not isinstance(other, self.__class__): | |
| return operator.eq(self.__class__, type(other)) | |
| return operator.eq(self.key, other.key) | |
| def __call__(self): | |
| """Return a strong reference to the bound method. | |
| If the target cannot be retrieved, then will return None, | |
| otherwise returns a bound instance method for our object and | |
| function. | |
| Note: You may call this method any number of times, as it does | |
| not invalidate the reference. | |
| """ | |
| target = self.weak_self() | |
| if target is not None: | |
| function = self.weak_func() | |
| if function is not None: | |
| return function.__get__(target) | |
| return None | |