17db96d56Sopenharmony_ci"""Weak reference support for Python. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciThis module is an implementation of PEP 205: 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_cihttps://peps.python.org/pep-0205/ 67db96d56Sopenharmony_ci""" 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ci# Naming convention: Variables named "wr" are weak reference objects; 97db96d56Sopenharmony_ci# they are called this instead of "ref" to avoid name collisions with 107db96d56Sopenharmony_ci# the module-global ref() function imported from _weakref. 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_cifrom _weakref import ( 137db96d56Sopenharmony_ci getweakrefcount, 147db96d56Sopenharmony_ci getweakrefs, 157db96d56Sopenharmony_ci ref, 167db96d56Sopenharmony_ci proxy, 177db96d56Sopenharmony_ci CallableProxyType, 187db96d56Sopenharmony_ci ProxyType, 197db96d56Sopenharmony_ci ReferenceType, 207db96d56Sopenharmony_ci _remove_dead_weakref) 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_cifrom _weakrefset import WeakSet, _IterationGuard 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_ciimport _collections_abc # Import after _weakref to avoid circular import. 257db96d56Sopenharmony_ciimport sys 267db96d56Sopenharmony_ciimport itertools 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ciProxyTypes = (ProxyType, CallableProxyType) 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", 317db96d56Sopenharmony_ci "WeakKeyDictionary", "ReferenceType", "ProxyType", 327db96d56Sopenharmony_ci "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 337db96d56Sopenharmony_ci "WeakSet", "WeakMethod", "finalize"] 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci_collections_abc.MutableSet.register(WeakSet) 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ciclass WeakMethod(ref): 397db96d56Sopenharmony_ci """ 407db96d56Sopenharmony_ci A custom `weakref.ref` subclass which simulates a weak reference to 417db96d56Sopenharmony_ci a bound method, working around the lifetime problem of bound methods. 427db96d56Sopenharmony_ci """ 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__" 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci def __new__(cls, meth, callback=None): 477db96d56Sopenharmony_ci try: 487db96d56Sopenharmony_ci obj = meth.__self__ 497db96d56Sopenharmony_ci func = meth.__func__ 507db96d56Sopenharmony_ci except AttributeError: 517db96d56Sopenharmony_ci raise TypeError("argument should be a bound method, not {}" 527db96d56Sopenharmony_ci .format(type(meth))) from None 537db96d56Sopenharmony_ci def _cb(arg): 547db96d56Sopenharmony_ci # The self-weakref trick is needed to avoid creating a reference 557db96d56Sopenharmony_ci # cycle. 567db96d56Sopenharmony_ci self = self_wr() 577db96d56Sopenharmony_ci if self._alive: 587db96d56Sopenharmony_ci self._alive = False 597db96d56Sopenharmony_ci if callback is not None: 607db96d56Sopenharmony_ci callback(self) 617db96d56Sopenharmony_ci self = ref.__new__(cls, obj, _cb) 627db96d56Sopenharmony_ci self._func_ref = ref(func, _cb) 637db96d56Sopenharmony_ci self._meth_type = type(meth) 647db96d56Sopenharmony_ci self._alive = True 657db96d56Sopenharmony_ci self_wr = ref(self) 667db96d56Sopenharmony_ci return self 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci def __call__(self): 697db96d56Sopenharmony_ci obj = super().__call__() 707db96d56Sopenharmony_ci func = self._func_ref() 717db96d56Sopenharmony_ci if obj is None or func is None: 727db96d56Sopenharmony_ci return None 737db96d56Sopenharmony_ci return self._meth_type(func, obj) 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci def __eq__(self, other): 767db96d56Sopenharmony_ci if isinstance(other, WeakMethod): 777db96d56Sopenharmony_ci if not self._alive or not other._alive: 787db96d56Sopenharmony_ci return self is other 797db96d56Sopenharmony_ci return ref.__eq__(self, other) and self._func_ref == other._func_ref 807db96d56Sopenharmony_ci return NotImplemented 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci def __ne__(self, other): 837db96d56Sopenharmony_ci if isinstance(other, WeakMethod): 847db96d56Sopenharmony_ci if not self._alive or not other._alive: 857db96d56Sopenharmony_ci return self is not other 867db96d56Sopenharmony_ci return ref.__ne__(self, other) or self._func_ref != other._func_ref 877db96d56Sopenharmony_ci return NotImplemented 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci __hash__ = ref.__hash__ 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ciclass WeakValueDictionary(_collections_abc.MutableMapping): 937db96d56Sopenharmony_ci """Mapping class that references values weakly. 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci Entries in the dictionary will be discarded when no strong 967db96d56Sopenharmony_ci reference to the value exists anymore 977db96d56Sopenharmony_ci """ 987db96d56Sopenharmony_ci # We inherit the constructor without worrying about the input 997db96d56Sopenharmony_ci # dictionary; since it uses our .update() method, we get the right 1007db96d56Sopenharmony_ci # checks (if the other dictionary is a WeakValueDictionary, 1017db96d56Sopenharmony_ci # objects are unwrapped on the way out, and we always wrap on the 1027db96d56Sopenharmony_ci # way in). 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci def __init__(self, other=(), /, **kw): 1057db96d56Sopenharmony_ci def remove(wr, selfref=ref(self), _atomic_removal=_remove_dead_weakref): 1067db96d56Sopenharmony_ci self = selfref() 1077db96d56Sopenharmony_ci if self is not None: 1087db96d56Sopenharmony_ci if self._iterating: 1097db96d56Sopenharmony_ci self._pending_removals.append(wr.key) 1107db96d56Sopenharmony_ci else: 1117db96d56Sopenharmony_ci # Atomic removal is necessary since this function 1127db96d56Sopenharmony_ci # can be called asynchronously by the GC 1137db96d56Sopenharmony_ci _atomic_removal(self.data, wr.key) 1147db96d56Sopenharmony_ci self._remove = remove 1157db96d56Sopenharmony_ci # A list of keys to be removed 1167db96d56Sopenharmony_ci self._pending_removals = [] 1177db96d56Sopenharmony_ci self._iterating = set() 1187db96d56Sopenharmony_ci self.data = {} 1197db96d56Sopenharmony_ci self.update(other, **kw) 1207db96d56Sopenharmony_ci 1217db96d56Sopenharmony_ci def _commit_removals(self, _atomic_removal=_remove_dead_weakref): 1227db96d56Sopenharmony_ci pop = self._pending_removals.pop 1237db96d56Sopenharmony_ci d = self.data 1247db96d56Sopenharmony_ci # We shouldn't encounter any KeyError, because this method should 1257db96d56Sopenharmony_ci # always be called *before* mutating the dict. 1267db96d56Sopenharmony_ci while True: 1277db96d56Sopenharmony_ci try: 1287db96d56Sopenharmony_ci key = pop() 1297db96d56Sopenharmony_ci except IndexError: 1307db96d56Sopenharmony_ci return 1317db96d56Sopenharmony_ci _atomic_removal(d, key) 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci def __getitem__(self, key): 1347db96d56Sopenharmony_ci if self._pending_removals: 1357db96d56Sopenharmony_ci self._commit_removals() 1367db96d56Sopenharmony_ci o = self.data[key]() 1377db96d56Sopenharmony_ci if o is None: 1387db96d56Sopenharmony_ci raise KeyError(key) 1397db96d56Sopenharmony_ci else: 1407db96d56Sopenharmony_ci return o 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci def __delitem__(self, key): 1437db96d56Sopenharmony_ci if self._pending_removals: 1447db96d56Sopenharmony_ci self._commit_removals() 1457db96d56Sopenharmony_ci del self.data[key] 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ci def __len__(self): 1487db96d56Sopenharmony_ci if self._pending_removals: 1497db96d56Sopenharmony_ci self._commit_removals() 1507db96d56Sopenharmony_ci return len(self.data) 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci def __contains__(self, key): 1537db96d56Sopenharmony_ci if self._pending_removals: 1547db96d56Sopenharmony_ci self._commit_removals() 1557db96d56Sopenharmony_ci try: 1567db96d56Sopenharmony_ci o = self.data[key]() 1577db96d56Sopenharmony_ci except KeyError: 1587db96d56Sopenharmony_ci return False 1597db96d56Sopenharmony_ci return o is not None 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ci def __repr__(self): 1627db96d56Sopenharmony_ci return "<%s at %#x>" % (self.__class__.__name__, id(self)) 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_ci def __setitem__(self, key, value): 1657db96d56Sopenharmony_ci if self._pending_removals: 1667db96d56Sopenharmony_ci self._commit_removals() 1677db96d56Sopenharmony_ci self.data[key] = KeyedRef(value, self._remove, key) 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def copy(self): 1707db96d56Sopenharmony_ci if self._pending_removals: 1717db96d56Sopenharmony_ci self._commit_removals() 1727db96d56Sopenharmony_ci new = WeakValueDictionary() 1737db96d56Sopenharmony_ci with _IterationGuard(self): 1747db96d56Sopenharmony_ci for key, wr in self.data.items(): 1757db96d56Sopenharmony_ci o = wr() 1767db96d56Sopenharmony_ci if o is not None: 1777db96d56Sopenharmony_ci new[key] = o 1787db96d56Sopenharmony_ci return new 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci __copy__ = copy 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci def __deepcopy__(self, memo): 1837db96d56Sopenharmony_ci from copy import deepcopy 1847db96d56Sopenharmony_ci if self._pending_removals: 1857db96d56Sopenharmony_ci self._commit_removals() 1867db96d56Sopenharmony_ci new = self.__class__() 1877db96d56Sopenharmony_ci with _IterationGuard(self): 1887db96d56Sopenharmony_ci for key, wr in self.data.items(): 1897db96d56Sopenharmony_ci o = wr() 1907db96d56Sopenharmony_ci if o is not None: 1917db96d56Sopenharmony_ci new[deepcopy(key, memo)] = o 1927db96d56Sopenharmony_ci return new 1937db96d56Sopenharmony_ci 1947db96d56Sopenharmony_ci def get(self, key, default=None): 1957db96d56Sopenharmony_ci if self._pending_removals: 1967db96d56Sopenharmony_ci self._commit_removals() 1977db96d56Sopenharmony_ci try: 1987db96d56Sopenharmony_ci wr = self.data[key] 1997db96d56Sopenharmony_ci except KeyError: 2007db96d56Sopenharmony_ci return default 2017db96d56Sopenharmony_ci else: 2027db96d56Sopenharmony_ci o = wr() 2037db96d56Sopenharmony_ci if o is None: 2047db96d56Sopenharmony_ci # This should only happen 2057db96d56Sopenharmony_ci return default 2067db96d56Sopenharmony_ci else: 2077db96d56Sopenharmony_ci return o 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci def items(self): 2107db96d56Sopenharmony_ci if self._pending_removals: 2117db96d56Sopenharmony_ci self._commit_removals() 2127db96d56Sopenharmony_ci with _IterationGuard(self): 2137db96d56Sopenharmony_ci for k, wr in self.data.items(): 2147db96d56Sopenharmony_ci v = wr() 2157db96d56Sopenharmony_ci if v is not None: 2167db96d56Sopenharmony_ci yield k, v 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci def keys(self): 2197db96d56Sopenharmony_ci if self._pending_removals: 2207db96d56Sopenharmony_ci self._commit_removals() 2217db96d56Sopenharmony_ci with _IterationGuard(self): 2227db96d56Sopenharmony_ci for k, wr in self.data.items(): 2237db96d56Sopenharmony_ci if wr() is not None: 2247db96d56Sopenharmony_ci yield k 2257db96d56Sopenharmony_ci 2267db96d56Sopenharmony_ci __iter__ = keys 2277db96d56Sopenharmony_ci 2287db96d56Sopenharmony_ci def itervaluerefs(self): 2297db96d56Sopenharmony_ci """Return an iterator that yields the weak references to the values. 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci The references are not guaranteed to be 'live' at the time 2327db96d56Sopenharmony_ci they are used, so the result of calling the references needs 2337db96d56Sopenharmony_ci to be checked before being used. This can be used to avoid 2347db96d56Sopenharmony_ci creating references that will cause the garbage collector to 2357db96d56Sopenharmony_ci keep the values around longer than needed. 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci """ 2387db96d56Sopenharmony_ci if self._pending_removals: 2397db96d56Sopenharmony_ci self._commit_removals() 2407db96d56Sopenharmony_ci with _IterationGuard(self): 2417db96d56Sopenharmony_ci yield from self.data.values() 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def values(self): 2447db96d56Sopenharmony_ci if self._pending_removals: 2457db96d56Sopenharmony_ci self._commit_removals() 2467db96d56Sopenharmony_ci with _IterationGuard(self): 2477db96d56Sopenharmony_ci for wr in self.data.values(): 2487db96d56Sopenharmony_ci obj = wr() 2497db96d56Sopenharmony_ci if obj is not None: 2507db96d56Sopenharmony_ci yield obj 2517db96d56Sopenharmony_ci 2527db96d56Sopenharmony_ci def popitem(self): 2537db96d56Sopenharmony_ci if self._pending_removals: 2547db96d56Sopenharmony_ci self._commit_removals() 2557db96d56Sopenharmony_ci while True: 2567db96d56Sopenharmony_ci key, wr = self.data.popitem() 2577db96d56Sopenharmony_ci o = wr() 2587db96d56Sopenharmony_ci if o is not None: 2597db96d56Sopenharmony_ci return key, o 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci def pop(self, key, *args): 2627db96d56Sopenharmony_ci if self._pending_removals: 2637db96d56Sopenharmony_ci self._commit_removals() 2647db96d56Sopenharmony_ci try: 2657db96d56Sopenharmony_ci o = self.data.pop(key)() 2667db96d56Sopenharmony_ci except KeyError: 2677db96d56Sopenharmony_ci o = None 2687db96d56Sopenharmony_ci if o is None: 2697db96d56Sopenharmony_ci if args: 2707db96d56Sopenharmony_ci return args[0] 2717db96d56Sopenharmony_ci else: 2727db96d56Sopenharmony_ci raise KeyError(key) 2737db96d56Sopenharmony_ci else: 2747db96d56Sopenharmony_ci return o 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ci def setdefault(self, key, default=None): 2777db96d56Sopenharmony_ci try: 2787db96d56Sopenharmony_ci o = self.data[key]() 2797db96d56Sopenharmony_ci except KeyError: 2807db96d56Sopenharmony_ci o = None 2817db96d56Sopenharmony_ci if o is None: 2827db96d56Sopenharmony_ci if self._pending_removals: 2837db96d56Sopenharmony_ci self._commit_removals() 2847db96d56Sopenharmony_ci self.data[key] = KeyedRef(default, self._remove, key) 2857db96d56Sopenharmony_ci return default 2867db96d56Sopenharmony_ci else: 2877db96d56Sopenharmony_ci return o 2887db96d56Sopenharmony_ci 2897db96d56Sopenharmony_ci def update(self, other=None, /, **kwargs): 2907db96d56Sopenharmony_ci if self._pending_removals: 2917db96d56Sopenharmony_ci self._commit_removals() 2927db96d56Sopenharmony_ci d = self.data 2937db96d56Sopenharmony_ci if other is not None: 2947db96d56Sopenharmony_ci if not hasattr(other, "items"): 2957db96d56Sopenharmony_ci other = dict(other) 2967db96d56Sopenharmony_ci for key, o in other.items(): 2977db96d56Sopenharmony_ci d[key] = KeyedRef(o, self._remove, key) 2987db96d56Sopenharmony_ci for key, o in kwargs.items(): 2997db96d56Sopenharmony_ci d[key] = KeyedRef(o, self._remove, key) 3007db96d56Sopenharmony_ci 3017db96d56Sopenharmony_ci def valuerefs(self): 3027db96d56Sopenharmony_ci """Return a list of weak references to the values. 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ci The references are not guaranteed to be 'live' at the time 3057db96d56Sopenharmony_ci they are used, so the result of calling the references needs 3067db96d56Sopenharmony_ci to be checked before being used. This can be used to avoid 3077db96d56Sopenharmony_ci creating references that will cause the garbage collector to 3087db96d56Sopenharmony_ci keep the values around longer than needed. 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci """ 3117db96d56Sopenharmony_ci if self._pending_removals: 3127db96d56Sopenharmony_ci self._commit_removals() 3137db96d56Sopenharmony_ci return list(self.data.values()) 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci def __ior__(self, other): 3167db96d56Sopenharmony_ci self.update(other) 3177db96d56Sopenharmony_ci return self 3187db96d56Sopenharmony_ci 3197db96d56Sopenharmony_ci def __or__(self, other): 3207db96d56Sopenharmony_ci if isinstance(other, _collections_abc.Mapping): 3217db96d56Sopenharmony_ci c = self.copy() 3227db96d56Sopenharmony_ci c.update(other) 3237db96d56Sopenharmony_ci return c 3247db96d56Sopenharmony_ci return NotImplemented 3257db96d56Sopenharmony_ci 3267db96d56Sopenharmony_ci def __ror__(self, other): 3277db96d56Sopenharmony_ci if isinstance(other, _collections_abc.Mapping): 3287db96d56Sopenharmony_ci c = self.__class__() 3297db96d56Sopenharmony_ci c.update(other) 3307db96d56Sopenharmony_ci c.update(self) 3317db96d56Sopenharmony_ci return c 3327db96d56Sopenharmony_ci return NotImplemented 3337db96d56Sopenharmony_ci 3347db96d56Sopenharmony_ci 3357db96d56Sopenharmony_ciclass KeyedRef(ref): 3367db96d56Sopenharmony_ci """Specialized reference that includes a key corresponding to the value. 3377db96d56Sopenharmony_ci 3387db96d56Sopenharmony_ci This is used in the WeakValueDictionary to avoid having to create 3397db96d56Sopenharmony_ci a function object for each key stored in the mapping. A shared 3407db96d56Sopenharmony_ci callback object can use the 'key' attribute of a KeyedRef instead 3417db96d56Sopenharmony_ci of getting a reference to the key from an enclosing scope. 3427db96d56Sopenharmony_ci 3437db96d56Sopenharmony_ci """ 3447db96d56Sopenharmony_ci 3457db96d56Sopenharmony_ci __slots__ = "key", 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci def __new__(type, ob, callback, key): 3487db96d56Sopenharmony_ci self = ref.__new__(type, ob, callback) 3497db96d56Sopenharmony_ci self.key = key 3507db96d56Sopenharmony_ci return self 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci def __init__(self, ob, callback, key): 3537db96d56Sopenharmony_ci super().__init__(ob, callback) 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci 3567db96d56Sopenharmony_ciclass WeakKeyDictionary(_collections_abc.MutableMapping): 3577db96d56Sopenharmony_ci """ Mapping class that references keys weakly. 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci Entries in the dictionary will be discarded when there is no 3607db96d56Sopenharmony_ci longer a strong reference to the key. This can be used to 3617db96d56Sopenharmony_ci associate additional data with an object owned by other parts of 3627db96d56Sopenharmony_ci an application without adding attributes to those objects. This 3637db96d56Sopenharmony_ci can be especially useful with objects that override attribute 3647db96d56Sopenharmony_ci accesses. 3657db96d56Sopenharmony_ci """ 3667db96d56Sopenharmony_ci 3677db96d56Sopenharmony_ci def __init__(self, dict=None): 3687db96d56Sopenharmony_ci self.data = {} 3697db96d56Sopenharmony_ci def remove(k, selfref=ref(self)): 3707db96d56Sopenharmony_ci self = selfref() 3717db96d56Sopenharmony_ci if self is not None: 3727db96d56Sopenharmony_ci if self._iterating: 3737db96d56Sopenharmony_ci self._pending_removals.append(k) 3747db96d56Sopenharmony_ci else: 3757db96d56Sopenharmony_ci try: 3767db96d56Sopenharmony_ci del self.data[k] 3777db96d56Sopenharmony_ci except KeyError: 3787db96d56Sopenharmony_ci pass 3797db96d56Sopenharmony_ci self._remove = remove 3807db96d56Sopenharmony_ci # A list of dead weakrefs (keys to be removed) 3817db96d56Sopenharmony_ci self._pending_removals = [] 3827db96d56Sopenharmony_ci self._iterating = set() 3837db96d56Sopenharmony_ci self._dirty_len = False 3847db96d56Sopenharmony_ci if dict is not None: 3857db96d56Sopenharmony_ci self.update(dict) 3867db96d56Sopenharmony_ci 3877db96d56Sopenharmony_ci def _commit_removals(self): 3887db96d56Sopenharmony_ci # NOTE: We don't need to call this method before mutating the dict, 3897db96d56Sopenharmony_ci # because a dead weakref never compares equal to a live weakref, 3907db96d56Sopenharmony_ci # even if they happened to refer to equal objects. 3917db96d56Sopenharmony_ci # However, it means keys may already have been removed. 3927db96d56Sopenharmony_ci pop = self._pending_removals.pop 3937db96d56Sopenharmony_ci d = self.data 3947db96d56Sopenharmony_ci while True: 3957db96d56Sopenharmony_ci try: 3967db96d56Sopenharmony_ci key = pop() 3977db96d56Sopenharmony_ci except IndexError: 3987db96d56Sopenharmony_ci return 3997db96d56Sopenharmony_ci 4007db96d56Sopenharmony_ci try: 4017db96d56Sopenharmony_ci del d[key] 4027db96d56Sopenharmony_ci except KeyError: 4037db96d56Sopenharmony_ci pass 4047db96d56Sopenharmony_ci 4057db96d56Sopenharmony_ci def _scrub_removals(self): 4067db96d56Sopenharmony_ci d = self.data 4077db96d56Sopenharmony_ci self._pending_removals = [k for k in self._pending_removals if k in d] 4087db96d56Sopenharmony_ci self._dirty_len = False 4097db96d56Sopenharmony_ci 4107db96d56Sopenharmony_ci def __delitem__(self, key): 4117db96d56Sopenharmony_ci self._dirty_len = True 4127db96d56Sopenharmony_ci del self.data[ref(key)] 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_ci def __getitem__(self, key): 4157db96d56Sopenharmony_ci return self.data[ref(key)] 4167db96d56Sopenharmony_ci 4177db96d56Sopenharmony_ci def __len__(self): 4187db96d56Sopenharmony_ci if self._dirty_len and self._pending_removals: 4197db96d56Sopenharmony_ci # self._pending_removals may still contain keys which were 4207db96d56Sopenharmony_ci # explicitly removed, we have to scrub them (see issue #21173). 4217db96d56Sopenharmony_ci self._scrub_removals() 4227db96d56Sopenharmony_ci return len(self.data) - len(self._pending_removals) 4237db96d56Sopenharmony_ci 4247db96d56Sopenharmony_ci def __repr__(self): 4257db96d56Sopenharmony_ci return "<%s at %#x>" % (self.__class__.__name__, id(self)) 4267db96d56Sopenharmony_ci 4277db96d56Sopenharmony_ci def __setitem__(self, key, value): 4287db96d56Sopenharmony_ci self.data[ref(key, self._remove)] = value 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci def copy(self): 4317db96d56Sopenharmony_ci new = WeakKeyDictionary() 4327db96d56Sopenharmony_ci with _IterationGuard(self): 4337db96d56Sopenharmony_ci for key, value in self.data.items(): 4347db96d56Sopenharmony_ci o = key() 4357db96d56Sopenharmony_ci if o is not None: 4367db96d56Sopenharmony_ci new[o] = value 4377db96d56Sopenharmony_ci return new 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci __copy__ = copy 4407db96d56Sopenharmony_ci 4417db96d56Sopenharmony_ci def __deepcopy__(self, memo): 4427db96d56Sopenharmony_ci from copy import deepcopy 4437db96d56Sopenharmony_ci new = self.__class__() 4447db96d56Sopenharmony_ci with _IterationGuard(self): 4457db96d56Sopenharmony_ci for key, value in self.data.items(): 4467db96d56Sopenharmony_ci o = key() 4477db96d56Sopenharmony_ci if o is not None: 4487db96d56Sopenharmony_ci new[o] = deepcopy(value, memo) 4497db96d56Sopenharmony_ci return new 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ci def get(self, key, default=None): 4527db96d56Sopenharmony_ci return self.data.get(ref(key),default) 4537db96d56Sopenharmony_ci 4547db96d56Sopenharmony_ci def __contains__(self, key): 4557db96d56Sopenharmony_ci try: 4567db96d56Sopenharmony_ci wr = ref(key) 4577db96d56Sopenharmony_ci except TypeError: 4587db96d56Sopenharmony_ci return False 4597db96d56Sopenharmony_ci return wr in self.data 4607db96d56Sopenharmony_ci 4617db96d56Sopenharmony_ci def items(self): 4627db96d56Sopenharmony_ci with _IterationGuard(self): 4637db96d56Sopenharmony_ci for wr, value in self.data.items(): 4647db96d56Sopenharmony_ci key = wr() 4657db96d56Sopenharmony_ci if key is not None: 4667db96d56Sopenharmony_ci yield key, value 4677db96d56Sopenharmony_ci 4687db96d56Sopenharmony_ci def keys(self): 4697db96d56Sopenharmony_ci with _IterationGuard(self): 4707db96d56Sopenharmony_ci for wr in self.data: 4717db96d56Sopenharmony_ci obj = wr() 4727db96d56Sopenharmony_ci if obj is not None: 4737db96d56Sopenharmony_ci yield obj 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci __iter__ = keys 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci def values(self): 4787db96d56Sopenharmony_ci with _IterationGuard(self): 4797db96d56Sopenharmony_ci for wr, value in self.data.items(): 4807db96d56Sopenharmony_ci if wr() is not None: 4817db96d56Sopenharmony_ci yield value 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci def keyrefs(self): 4847db96d56Sopenharmony_ci """Return a list of weak references to the keys. 4857db96d56Sopenharmony_ci 4867db96d56Sopenharmony_ci The references are not guaranteed to be 'live' at the time 4877db96d56Sopenharmony_ci they are used, so the result of calling the references needs 4887db96d56Sopenharmony_ci to be checked before being used. This can be used to avoid 4897db96d56Sopenharmony_ci creating references that will cause the garbage collector to 4907db96d56Sopenharmony_ci keep the keys around longer than needed. 4917db96d56Sopenharmony_ci 4927db96d56Sopenharmony_ci """ 4937db96d56Sopenharmony_ci return list(self.data) 4947db96d56Sopenharmony_ci 4957db96d56Sopenharmony_ci def popitem(self): 4967db96d56Sopenharmony_ci self._dirty_len = True 4977db96d56Sopenharmony_ci while True: 4987db96d56Sopenharmony_ci key, value = self.data.popitem() 4997db96d56Sopenharmony_ci o = key() 5007db96d56Sopenharmony_ci if o is not None: 5017db96d56Sopenharmony_ci return o, value 5027db96d56Sopenharmony_ci 5037db96d56Sopenharmony_ci def pop(self, key, *args): 5047db96d56Sopenharmony_ci self._dirty_len = True 5057db96d56Sopenharmony_ci return self.data.pop(ref(key), *args) 5067db96d56Sopenharmony_ci 5077db96d56Sopenharmony_ci def setdefault(self, key, default=None): 5087db96d56Sopenharmony_ci return self.data.setdefault(ref(key, self._remove),default) 5097db96d56Sopenharmony_ci 5107db96d56Sopenharmony_ci def update(self, dict=None, /, **kwargs): 5117db96d56Sopenharmony_ci d = self.data 5127db96d56Sopenharmony_ci if dict is not None: 5137db96d56Sopenharmony_ci if not hasattr(dict, "items"): 5147db96d56Sopenharmony_ci dict = type({})(dict) 5157db96d56Sopenharmony_ci for key, value in dict.items(): 5167db96d56Sopenharmony_ci d[ref(key, self._remove)] = value 5177db96d56Sopenharmony_ci if len(kwargs): 5187db96d56Sopenharmony_ci self.update(kwargs) 5197db96d56Sopenharmony_ci 5207db96d56Sopenharmony_ci def __ior__(self, other): 5217db96d56Sopenharmony_ci self.update(other) 5227db96d56Sopenharmony_ci return self 5237db96d56Sopenharmony_ci 5247db96d56Sopenharmony_ci def __or__(self, other): 5257db96d56Sopenharmony_ci if isinstance(other, _collections_abc.Mapping): 5267db96d56Sopenharmony_ci c = self.copy() 5277db96d56Sopenharmony_ci c.update(other) 5287db96d56Sopenharmony_ci return c 5297db96d56Sopenharmony_ci return NotImplemented 5307db96d56Sopenharmony_ci 5317db96d56Sopenharmony_ci def __ror__(self, other): 5327db96d56Sopenharmony_ci if isinstance(other, _collections_abc.Mapping): 5337db96d56Sopenharmony_ci c = self.__class__() 5347db96d56Sopenharmony_ci c.update(other) 5357db96d56Sopenharmony_ci c.update(self) 5367db96d56Sopenharmony_ci return c 5377db96d56Sopenharmony_ci return NotImplemented 5387db96d56Sopenharmony_ci 5397db96d56Sopenharmony_ci 5407db96d56Sopenharmony_ciclass finalize: 5417db96d56Sopenharmony_ci """Class for finalization of weakrefable objects 5427db96d56Sopenharmony_ci 5437db96d56Sopenharmony_ci finalize(obj, func, *args, **kwargs) returns a callable finalizer 5447db96d56Sopenharmony_ci object which will be called when obj is garbage collected. The 5457db96d56Sopenharmony_ci first time the finalizer is called it evaluates func(*arg, **kwargs) 5467db96d56Sopenharmony_ci and returns the result. After this the finalizer is dead, and 5477db96d56Sopenharmony_ci calling it just returns None. 5487db96d56Sopenharmony_ci 5497db96d56Sopenharmony_ci When the program exits any remaining finalizers for which the 5507db96d56Sopenharmony_ci atexit attribute is true will be run in reverse order of creation. 5517db96d56Sopenharmony_ci By default atexit is true. 5527db96d56Sopenharmony_ci """ 5537db96d56Sopenharmony_ci 5547db96d56Sopenharmony_ci # Finalizer objects don't have any state of their own. They are 5557db96d56Sopenharmony_ci # just used as keys to lookup _Info objects in the registry. This 5567db96d56Sopenharmony_ci # ensures that they cannot be part of a ref-cycle. 5577db96d56Sopenharmony_ci 5587db96d56Sopenharmony_ci __slots__ = () 5597db96d56Sopenharmony_ci _registry = {} 5607db96d56Sopenharmony_ci _shutdown = False 5617db96d56Sopenharmony_ci _index_iter = itertools.count() 5627db96d56Sopenharmony_ci _dirty = False 5637db96d56Sopenharmony_ci _registered_with_atexit = False 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci class _Info: 5667db96d56Sopenharmony_ci __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") 5677db96d56Sopenharmony_ci 5687db96d56Sopenharmony_ci def __init__(self, obj, func, /, *args, **kwargs): 5697db96d56Sopenharmony_ci if not self._registered_with_atexit: 5707db96d56Sopenharmony_ci # We may register the exit function more than once because 5717db96d56Sopenharmony_ci # of a thread race, but that is harmless 5727db96d56Sopenharmony_ci import atexit 5737db96d56Sopenharmony_ci atexit.register(self._exitfunc) 5747db96d56Sopenharmony_ci finalize._registered_with_atexit = True 5757db96d56Sopenharmony_ci info = self._Info() 5767db96d56Sopenharmony_ci info.weakref = ref(obj, self) 5777db96d56Sopenharmony_ci info.func = func 5787db96d56Sopenharmony_ci info.args = args 5797db96d56Sopenharmony_ci info.kwargs = kwargs or None 5807db96d56Sopenharmony_ci info.atexit = True 5817db96d56Sopenharmony_ci info.index = next(self._index_iter) 5827db96d56Sopenharmony_ci self._registry[self] = info 5837db96d56Sopenharmony_ci finalize._dirty = True 5847db96d56Sopenharmony_ci 5857db96d56Sopenharmony_ci def __call__(self, _=None): 5867db96d56Sopenharmony_ci """If alive then mark as dead and return func(*args, **kwargs); 5877db96d56Sopenharmony_ci otherwise return None""" 5887db96d56Sopenharmony_ci info = self._registry.pop(self, None) 5897db96d56Sopenharmony_ci if info and not self._shutdown: 5907db96d56Sopenharmony_ci return info.func(*info.args, **(info.kwargs or {})) 5917db96d56Sopenharmony_ci 5927db96d56Sopenharmony_ci def detach(self): 5937db96d56Sopenharmony_ci """If alive then mark as dead and return (obj, func, args, kwargs); 5947db96d56Sopenharmony_ci otherwise return None""" 5957db96d56Sopenharmony_ci info = self._registry.get(self) 5967db96d56Sopenharmony_ci obj = info and info.weakref() 5977db96d56Sopenharmony_ci if obj is not None and self._registry.pop(self, None): 5987db96d56Sopenharmony_ci return (obj, info.func, info.args, info.kwargs or {}) 5997db96d56Sopenharmony_ci 6007db96d56Sopenharmony_ci def peek(self): 6017db96d56Sopenharmony_ci """If alive then return (obj, func, args, kwargs); 6027db96d56Sopenharmony_ci otherwise return None""" 6037db96d56Sopenharmony_ci info = self._registry.get(self) 6047db96d56Sopenharmony_ci obj = info and info.weakref() 6057db96d56Sopenharmony_ci if obj is not None: 6067db96d56Sopenharmony_ci return (obj, info.func, info.args, info.kwargs or {}) 6077db96d56Sopenharmony_ci 6087db96d56Sopenharmony_ci @property 6097db96d56Sopenharmony_ci def alive(self): 6107db96d56Sopenharmony_ci """Whether finalizer is alive""" 6117db96d56Sopenharmony_ci return self in self._registry 6127db96d56Sopenharmony_ci 6137db96d56Sopenharmony_ci @property 6147db96d56Sopenharmony_ci def atexit(self): 6157db96d56Sopenharmony_ci """Whether finalizer should be called at exit""" 6167db96d56Sopenharmony_ci info = self._registry.get(self) 6177db96d56Sopenharmony_ci return bool(info) and info.atexit 6187db96d56Sopenharmony_ci 6197db96d56Sopenharmony_ci @atexit.setter 6207db96d56Sopenharmony_ci def atexit(self, value): 6217db96d56Sopenharmony_ci info = self._registry.get(self) 6227db96d56Sopenharmony_ci if info: 6237db96d56Sopenharmony_ci info.atexit = bool(value) 6247db96d56Sopenharmony_ci 6257db96d56Sopenharmony_ci def __repr__(self): 6267db96d56Sopenharmony_ci info = self._registry.get(self) 6277db96d56Sopenharmony_ci obj = info and info.weakref() 6287db96d56Sopenharmony_ci if obj is None: 6297db96d56Sopenharmony_ci return '<%s object at %#x; dead>' % (type(self).__name__, id(self)) 6307db96d56Sopenharmony_ci else: 6317db96d56Sopenharmony_ci return '<%s object at %#x; for %r at %#x>' % \ 6327db96d56Sopenharmony_ci (type(self).__name__, id(self), type(obj).__name__, id(obj)) 6337db96d56Sopenharmony_ci 6347db96d56Sopenharmony_ci @classmethod 6357db96d56Sopenharmony_ci def _select_for_exit(cls): 6367db96d56Sopenharmony_ci # Return live finalizers marked for exit, oldest first 6377db96d56Sopenharmony_ci L = [(f,i) for (f,i) in cls._registry.items() if i.atexit] 6387db96d56Sopenharmony_ci L.sort(key=lambda item:item[1].index) 6397db96d56Sopenharmony_ci return [f for (f,i) in L] 6407db96d56Sopenharmony_ci 6417db96d56Sopenharmony_ci @classmethod 6427db96d56Sopenharmony_ci def _exitfunc(cls): 6437db96d56Sopenharmony_ci # At shutdown invoke finalizers for which atexit is true. 6447db96d56Sopenharmony_ci # This is called once all other non-daemonic threads have been 6457db96d56Sopenharmony_ci # joined. 6467db96d56Sopenharmony_ci reenable_gc = False 6477db96d56Sopenharmony_ci try: 6487db96d56Sopenharmony_ci if cls._registry: 6497db96d56Sopenharmony_ci import gc 6507db96d56Sopenharmony_ci if gc.isenabled(): 6517db96d56Sopenharmony_ci reenable_gc = True 6527db96d56Sopenharmony_ci gc.disable() 6537db96d56Sopenharmony_ci pending = None 6547db96d56Sopenharmony_ci while True: 6557db96d56Sopenharmony_ci if pending is None or finalize._dirty: 6567db96d56Sopenharmony_ci pending = cls._select_for_exit() 6577db96d56Sopenharmony_ci finalize._dirty = False 6587db96d56Sopenharmony_ci if not pending: 6597db96d56Sopenharmony_ci break 6607db96d56Sopenharmony_ci f = pending.pop() 6617db96d56Sopenharmony_ci try: 6627db96d56Sopenharmony_ci # gc is disabled, so (assuming no daemonic 6637db96d56Sopenharmony_ci # threads) the following is the only line in 6647db96d56Sopenharmony_ci # this function which might trigger creation 6657db96d56Sopenharmony_ci # of a new finalizer 6667db96d56Sopenharmony_ci f() 6677db96d56Sopenharmony_ci except Exception: 6687db96d56Sopenharmony_ci sys.excepthook(*sys.exc_info()) 6697db96d56Sopenharmony_ci assert f not in cls._registry 6707db96d56Sopenharmony_ci finally: 6717db96d56Sopenharmony_ci # prevent any more finalizers from executing during shutdown 6727db96d56Sopenharmony_ci finalize._shutdown = True 6737db96d56Sopenharmony_ci if reenable_gc: 6747db96d56Sopenharmony_ci gc.enable() 675