(root)/
Python-3.12.0/
Lib/
weakref.py
       1  """Weak reference support for Python.
       2  
       3  This module is an implementation of PEP 205:
       4  
       5  https://peps.python.org/pep-0205/
       6  """
       7  
       8  # Naming convention: Variables named "wr" are weak reference objects;
       9  # they are called this instead of "ref" to avoid name collisions with
      10  # the module-global ref() function imported from _weakref.
      11  
      12  from _weakref import (
      13       getweakrefcount,
      14       getweakrefs,
      15       ref,
      16       proxy,
      17       CallableProxyType,
      18       ProxyType,
      19       ReferenceType,
      20       _remove_dead_weakref)
      21  
      22  from _weakrefset import WeakSet, _IterationGuard
      23  
      24  import _collections_abc  # Import after _weakref to avoid circular import.
      25  import sys
      26  import itertools
      27  
      28  ProxyTypes = (ProxyType, CallableProxyType)
      29  
      30  __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
      31             "WeakKeyDictionary", "ReferenceType", "ProxyType",
      32             "CallableProxyType", "ProxyTypes", "WeakValueDictionary",
      33             "WeakSet", "WeakMethod", "finalize"]
      34  
      35  
      36  _collections_abc.MutableSet.register(WeakSet)
      37  
      38  class ESC[4;38;5;81mWeakMethod(ESC[4;38;5;149mref):
      39      """
      40      A custom `weakref.ref` subclass which simulates a weak reference to
      41      a bound method, working around the lifetime problem of bound methods.
      42      """
      43  
      44      __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__"
      45  
      46      def __new__(cls, meth, callback=None):
      47          try:
      48              obj = meth.__self__
      49              func = meth.__func__
      50          except AttributeError:
      51              raise TypeError("argument should be a bound method, not {}"
      52                              .format(type(meth))) from None
      53          def _cb(arg):
      54              # The self-weakref trick is needed to avoid creating a reference
      55              # cycle.
      56              self = self_wr()
      57              if self._alive:
      58                  self._alive = False
      59                  if callback is not None:
      60                      callback(self)
      61          self = ref.__new__(cls, obj, _cb)
      62          self._func_ref = ref(func, _cb)
      63          self._meth_type = type(meth)
      64          self._alive = True
      65          self_wr = ref(self)
      66          return self
      67  
      68      def __call__(self):
      69          obj = super().__call__()
      70          func = self._func_ref()
      71          if obj is None or func is None:
      72              return None
      73          return self._meth_type(func, obj)
      74  
      75      def __eq__(self, other):
      76          if isinstance(other, WeakMethod):
      77              if not self._alive or not other._alive:
      78                  return self is other
      79              return ref.__eq__(self, other) and self._func_ref == other._func_ref
      80          return NotImplemented
      81  
      82      def __ne__(self, other):
      83          if isinstance(other, WeakMethod):
      84              if not self._alive or not other._alive:
      85                  return self is not other
      86              return ref.__ne__(self, other) or self._func_ref != other._func_ref
      87          return NotImplemented
      88  
      89      __hash__ = ref.__hash__
      90  
      91  
      92  class ESC[4;38;5;81mWeakValueDictionary(ESC[4;38;5;149m_collections_abcESC[4;38;5;149m.ESC[4;38;5;149mMutableMapping):
      93      """Mapping class that references values weakly.
      94  
      95      Entries in the dictionary will be discarded when no strong
      96      reference to the value exists anymore
      97      """
      98      # We inherit the constructor without worrying about the input
      99      # dictionary; since it uses our .update() method, we get the right
     100      # checks (if the other dictionary is a WeakValueDictionary,
     101      # objects are unwrapped on the way out, and we always wrap on the
     102      # way in).
     103  
     104      def __init__(self, other=(), /, **kw):
     105          def remove(wr, selfref=ref(self), _atomic_removal=_remove_dead_weakref):
     106              self = selfref()
     107              if self is not None:
     108                  if self._iterating:
     109                      self._pending_removals.append(wr.key)
     110                  else:
     111                      # Atomic removal is necessary since this function
     112                      # can be called asynchronously by the GC
     113                      _atomic_removal(self.data, wr.key)
     114          self._remove = remove
     115          # A list of keys to be removed
     116          self._pending_removals = []
     117          self._iterating = set()
     118          self.data = {}
     119          self.update(other, **kw)
     120  
     121      def _commit_removals(self, _atomic_removal=_remove_dead_weakref):
     122          pop = self._pending_removals.pop
     123          d = self.data
     124          # We shouldn't encounter any KeyError, because this method should
     125          # always be called *before* mutating the dict.
     126          while True:
     127              try:
     128                  key = pop()
     129              except IndexError:
     130                  return
     131              _atomic_removal(d, key)
     132  
     133      def __getitem__(self, key):
     134          if self._pending_removals:
     135              self._commit_removals()
     136          o = self.data[key]()
     137          if o is None:
     138              raise KeyError(key)
     139          else:
     140              return o
     141  
     142      def __delitem__(self, key):
     143          if self._pending_removals:
     144              self._commit_removals()
     145          del self.data[key]
     146  
     147      def __len__(self):
     148          if self._pending_removals:
     149              self._commit_removals()
     150          return len(self.data)
     151  
     152      def __contains__(self, key):
     153          if self._pending_removals:
     154              self._commit_removals()
     155          try:
     156              o = self.data[key]()
     157          except KeyError:
     158              return False
     159          return o is not None
     160  
     161      def __repr__(self):
     162          return "<%s at %#x>" % (self.__class__.__name__, id(self))
     163  
     164      def __setitem__(self, key, value):
     165          if self._pending_removals:
     166              self._commit_removals()
     167          self.data[key] = KeyedRef(value, self._remove, key)
     168  
     169      def copy(self):
     170          if self._pending_removals:
     171              self._commit_removals()
     172          new = WeakValueDictionary()
     173          with _IterationGuard(self):
     174              for key, wr in self.data.items():
     175                  o = wr()
     176                  if o is not None:
     177                      new[key] = o
     178          return new
     179  
     180      __copy__ = copy
     181  
     182      def __deepcopy__(self, memo):
     183          from copy import deepcopy
     184          if self._pending_removals:
     185              self._commit_removals()
     186          new = self.__class__()
     187          with _IterationGuard(self):
     188              for key, wr in self.data.items():
     189                  o = wr()
     190                  if o is not None:
     191                      new[deepcopy(key, memo)] = o
     192          return new
     193  
     194      def get(self, key, default=None):
     195          if self._pending_removals:
     196              self._commit_removals()
     197          try:
     198              wr = self.data[key]
     199          except KeyError:
     200              return default
     201          else:
     202              o = wr()
     203              if o is None:
     204                  # This should only happen
     205                  return default
     206              else:
     207                  return o
     208  
     209      def items(self):
     210          if self._pending_removals:
     211              self._commit_removals()
     212          with _IterationGuard(self):
     213              for k, wr in self.data.items():
     214                  v = wr()
     215                  if v is not None:
     216                      yield k, v
     217  
     218      def keys(self):
     219          if self._pending_removals:
     220              self._commit_removals()
     221          with _IterationGuard(self):
     222              for k, wr in self.data.items():
     223                  if wr() is not None:
     224                      yield k
     225  
     226      __iter__ = keys
     227  
     228      def itervaluerefs(self):
     229          """Return an iterator that yields the weak references to the values.
     230  
     231          The references are not guaranteed to be 'live' at the time
     232          they are used, so the result of calling the references needs
     233          to be checked before being used.  This can be used to avoid
     234          creating references that will cause the garbage collector to
     235          keep the values around longer than needed.
     236  
     237          """
     238          if self._pending_removals:
     239              self._commit_removals()
     240          with _IterationGuard(self):
     241              yield from self.data.values()
     242  
     243      def values(self):
     244          if self._pending_removals:
     245              self._commit_removals()
     246          with _IterationGuard(self):
     247              for wr in self.data.values():
     248                  obj = wr()
     249                  if obj is not None:
     250                      yield obj
     251  
     252      def popitem(self):
     253          if self._pending_removals:
     254              self._commit_removals()
     255          while True:
     256              key, wr = self.data.popitem()
     257              o = wr()
     258              if o is not None:
     259                  return key, o
     260  
     261      def pop(self, key, *args):
     262          if self._pending_removals:
     263              self._commit_removals()
     264          try:
     265              o = self.data.pop(key)()
     266          except KeyError:
     267              o = None
     268          if o is None:
     269              if args:
     270                  return args[0]
     271              else:
     272                  raise KeyError(key)
     273          else:
     274              return o
     275  
     276      def setdefault(self, key, default=None):
     277          try:
     278              o = self.data[key]()
     279          except KeyError:
     280              o = None
     281          if o is None:
     282              if self._pending_removals:
     283                  self._commit_removals()
     284              self.data[key] = KeyedRef(default, self._remove, key)
     285              return default
     286          else:
     287              return o
     288  
     289      def update(self, other=None, /, **kwargs):
     290          if self._pending_removals:
     291              self._commit_removals()
     292          d = self.data
     293          if other is not None:
     294              if not hasattr(other, "items"):
     295                  other = dict(other)
     296              for key, o in other.items():
     297                  d[key] = KeyedRef(o, self._remove, key)
     298          for key, o in kwargs.items():
     299              d[key] = KeyedRef(o, self._remove, key)
     300  
     301      def valuerefs(self):
     302          """Return a list of weak references to the values.
     303  
     304          The references are not guaranteed to be 'live' at the time
     305          they are used, so the result of calling the references needs
     306          to be checked before being used.  This can be used to avoid
     307          creating references that will cause the garbage collector to
     308          keep the values around longer than needed.
     309  
     310          """
     311          if self._pending_removals:
     312              self._commit_removals()
     313          return list(self.data.values())
     314  
     315      def __ior__(self, other):
     316          self.update(other)
     317          return self
     318  
     319      def __or__(self, other):
     320          if isinstance(other, _collections_abc.Mapping):
     321              c = self.copy()
     322              c.update(other)
     323              return c
     324          return NotImplemented
     325  
     326      def __ror__(self, other):
     327          if isinstance(other, _collections_abc.Mapping):
     328              c = self.__class__()
     329              c.update(other)
     330              c.update(self)
     331              return c
     332          return NotImplemented
     333  
     334  
     335  class ESC[4;38;5;81mKeyedRef(ESC[4;38;5;149mref):
     336      """Specialized reference that includes a key corresponding to the value.
     337  
     338      This is used in the WeakValueDictionary to avoid having to create
     339      a function object for each key stored in the mapping.  A shared
     340      callback object can use the 'key' attribute of a KeyedRef instead
     341      of getting a reference to the key from an enclosing scope.
     342  
     343      """
     344  
     345      __slots__ = "key",
     346  
     347      def __new__(type, ob, callback, key):
     348          self = ref.__new__(type, ob, callback)
     349          self.key = key
     350          return self
     351  
     352      def __init__(self, ob, callback, key):
     353          super().__init__(ob, callback)
     354  
     355  
     356  class ESC[4;38;5;81mWeakKeyDictionary(ESC[4;38;5;149m_collections_abcESC[4;38;5;149m.ESC[4;38;5;149mMutableMapping):
     357      """ Mapping class that references keys weakly.
     358  
     359      Entries in the dictionary will be discarded when there is no
     360      longer a strong reference to the key. This can be used to
     361      associate additional data with an object owned by other parts of
     362      an application without adding attributes to those objects. This
     363      can be especially useful with objects that override attribute
     364      accesses.
     365      """
     366  
     367      def __init__(self, dict=None):
     368          self.data = {}
     369          def remove(k, selfref=ref(self)):
     370              self = selfref()
     371              if self is not None:
     372                  if self._iterating:
     373                      self._pending_removals.append(k)
     374                  else:
     375                      try:
     376                          del self.data[k]
     377                      except KeyError:
     378                          pass
     379          self._remove = remove
     380          # A list of dead weakrefs (keys to be removed)
     381          self._pending_removals = []
     382          self._iterating = set()
     383          self._dirty_len = False
     384          if dict is not None:
     385              self.update(dict)
     386  
     387      def _commit_removals(self):
     388          # NOTE: We don't need to call this method before mutating the dict,
     389          # because a dead weakref never compares equal to a live weakref,
     390          # even if they happened to refer to equal objects.
     391          # However, it means keys may already have been removed.
     392          pop = self._pending_removals.pop
     393          d = self.data
     394          while True:
     395              try:
     396                  key = pop()
     397              except IndexError:
     398                  return
     399  
     400              try:
     401                  del d[key]
     402              except KeyError:
     403                  pass
     404  
     405      def _scrub_removals(self):
     406          d = self.data
     407          self._pending_removals = [k for k in self._pending_removals if k in d]
     408          self._dirty_len = False
     409  
     410      def __delitem__(self, key):
     411          self._dirty_len = True
     412          del self.data[ref(key)]
     413  
     414      def __getitem__(self, key):
     415          return self.data[ref(key)]
     416  
     417      def __len__(self):
     418          if self._dirty_len and self._pending_removals:
     419              # self._pending_removals may still contain keys which were
     420              # explicitly removed, we have to scrub them (see issue #21173).
     421              self._scrub_removals()
     422          return len(self.data) - len(self._pending_removals)
     423  
     424      def __repr__(self):
     425          return "<%s at %#x>" % (self.__class__.__name__, id(self))
     426  
     427      def __setitem__(self, key, value):
     428          self.data[ref(key, self._remove)] = value
     429  
     430      def copy(self):
     431          new = WeakKeyDictionary()
     432          with _IterationGuard(self):
     433              for key, value in self.data.items():
     434                  o = key()
     435                  if o is not None:
     436                      new[o] = value
     437          return new
     438  
     439      __copy__ = copy
     440  
     441      def __deepcopy__(self, memo):
     442          from copy import deepcopy
     443          new = self.__class__()
     444          with _IterationGuard(self):
     445              for key, value in self.data.items():
     446                  o = key()
     447                  if o is not None:
     448                      new[o] = deepcopy(value, memo)
     449          return new
     450  
     451      def get(self, key, default=None):
     452          return self.data.get(ref(key),default)
     453  
     454      def __contains__(self, key):
     455          try:
     456              wr = ref(key)
     457          except TypeError:
     458              return False
     459          return wr in self.data
     460  
     461      def items(self):
     462          with _IterationGuard(self):
     463              for wr, value in self.data.items():
     464                  key = wr()
     465                  if key is not None:
     466                      yield key, value
     467  
     468      def keys(self):
     469          with _IterationGuard(self):
     470              for wr in self.data:
     471                  obj = wr()
     472                  if obj is not None:
     473                      yield obj
     474  
     475      __iter__ = keys
     476  
     477      def values(self):
     478          with _IterationGuard(self):
     479              for wr, value in self.data.items():
     480                  if wr() is not None:
     481                      yield value
     482  
     483      def keyrefs(self):
     484          """Return a list of weak references to the keys.
     485  
     486          The references are not guaranteed to be 'live' at the time
     487          they are used, so the result of calling the references needs
     488          to be checked before being used.  This can be used to avoid
     489          creating references that will cause the garbage collector to
     490          keep the keys around longer than needed.
     491  
     492          """
     493          return list(self.data)
     494  
     495      def popitem(self):
     496          self._dirty_len = True
     497          while True:
     498              key, value = self.data.popitem()
     499              o = key()
     500              if o is not None:
     501                  return o, value
     502  
     503      def pop(self, key, *args):
     504          self._dirty_len = True
     505          return self.data.pop(ref(key), *args)
     506  
     507      def setdefault(self, key, default=None):
     508          return self.data.setdefault(ref(key, self._remove),default)
     509  
     510      def update(self, dict=None, /, **kwargs):
     511          d = self.data
     512          if dict is not None:
     513              if not hasattr(dict, "items"):
     514                  dict = type({})(dict)
     515              for key, value in dict.items():
     516                  d[ref(key, self._remove)] = value
     517          if len(kwargs):
     518              self.update(kwargs)
     519  
     520      def __ior__(self, other):
     521          self.update(other)
     522          return self
     523  
     524      def __or__(self, other):
     525          if isinstance(other, _collections_abc.Mapping):
     526              c = self.copy()
     527              c.update(other)
     528              return c
     529          return NotImplemented
     530  
     531      def __ror__(self, other):
     532          if isinstance(other, _collections_abc.Mapping):
     533              c = self.__class__()
     534              c.update(other)
     535              c.update(self)
     536              return c
     537          return NotImplemented
     538  
     539  
     540  class ESC[4;38;5;81mfinalize:
     541      """Class for finalization of weakrefable objects
     542  
     543      finalize(obj, func, *args, **kwargs) returns a callable finalizer
     544      object which will be called when obj is garbage collected. The
     545      first time the finalizer is called it evaluates func(*arg, **kwargs)
     546      and returns the result. After this the finalizer is dead, and
     547      calling it just returns None.
     548  
     549      When the program exits any remaining finalizers for which the
     550      atexit attribute is true will be run in reverse order of creation.
     551      By default atexit is true.
     552      """
     553  
     554      # Finalizer objects don't have any state of their own.  They are
     555      # just used as keys to lookup _Info objects in the registry.  This
     556      # ensures that they cannot be part of a ref-cycle.
     557  
     558      __slots__ = ()
     559      _registry = {}
     560      _shutdown = False
     561      _index_iter = itertools.count()
     562      _dirty = False
     563      _registered_with_atexit = False
     564  
     565      class ESC[4;38;5;81m_Info:
     566          __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
     567  
     568      def __init__(self, obj, func, /, *args, **kwargs):
     569          if not self._registered_with_atexit:
     570              # We may register the exit function more than once because
     571              # of a thread race, but that is harmless
     572              import atexit
     573              atexit.register(self._exitfunc)
     574              finalize._registered_with_atexit = True
     575          info = self._Info()
     576          info.weakref = ref(obj, self)
     577          info.func = func
     578          info.args = args
     579          info.kwargs = kwargs or None
     580          info.atexit = True
     581          info.index = next(self._index_iter)
     582          self._registry[self] = info
     583          finalize._dirty = True
     584  
     585      def __call__(self, _=None):
     586          """If alive then mark as dead and return func(*args, **kwargs);
     587          otherwise return None"""
     588          info = self._registry.pop(self, None)
     589          if info and not self._shutdown:
     590              return info.func(*info.args, **(info.kwargs or {}))
     591  
     592      def detach(self):
     593          """If alive then mark as dead and return (obj, func, args, kwargs);
     594          otherwise return None"""
     595          info = self._registry.get(self)
     596          obj = info and info.weakref()
     597          if obj is not None and self._registry.pop(self, None):
     598              return (obj, info.func, info.args, info.kwargs or {})
     599  
     600      def peek(self):
     601          """If alive then return (obj, func, args, kwargs);
     602          otherwise return None"""
     603          info = self._registry.get(self)
     604          obj = info and info.weakref()
     605          if obj is not None:
     606              return (obj, info.func, info.args, info.kwargs or {})
     607  
     608      @property
     609      def alive(self):
     610          """Whether finalizer is alive"""
     611          return self in self._registry
     612  
     613      @property
     614      def atexit(self):
     615          """Whether finalizer should be called at exit"""
     616          info = self._registry.get(self)
     617          return bool(info) and info.atexit
     618  
     619      @atexit.setter
     620      def atexit(self, value):
     621          info = self._registry.get(self)
     622          if info:
     623              info.atexit = bool(value)
     624  
     625      def __repr__(self):
     626          info = self._registry.get(self)
     627          obj = info and info.weakref()
     628          if obj is None:
     629              return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
     630          else:
     631              return '<%s object at %#x; for %r at %#x>' % \
     632                  (type(self).__name__, id(self), type(obj).__name__, id(obj))
     633  
     634      @classmethod
     635      def _select_for_exit(cls):
     636          # Return live finalizers marked for exit, oldest first
     637          L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
     638          L.sort(key=lambda item:item[1].index)
     639          return [f for (f,i) in L]
     640  
     641      @classmethod
     642      def _exitfunc(cls):
     643          # At shutdown invoke finalizers for which atexit is true.
     644          # This is called once all other non-daemonic threads have been
     645          # joined.
     646          reenable_gc = False
     647          try:
     648              if cls._registry:
     649                  import gc
     650                  if gc.isenabled():
     651                      reenable_gc = True
     652                      gc.disable()
     653                  pending = None
     654                  while True:
     655                      if pending is None or finalize._dirty:
     656                          pending = cls._select_for_exit()
     657                          finalize._dirty = False
     658                      if not pending:
     659                          break
     660                      f = pending.pop()
     661                      try:
     662                          # gc is disabled, so (assuming no daemonic
     663                          # threads) the following is the only line in
     664                          # this function which might trigger creation
     665                          # of a new finalizer
     666                          f()
     667                      except Exception:
     668                          sys.excepthook(*sys.exc_info())
     669                      assert f not in cls._registry
     670          finally:
     671              # prevent any more finalizers from executing during shutdown
     672              finalize._shutdown = True
     673              if reenable_gc:
     674                  gc.enable()