weakref_finalize.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # -*- coding: utf-8 -*-
  2. """
  3. backports.weakref_finalize
  4. ~~~~~~~~~~~~~~~~~~
  5. Backports the Python 3 ``weakref.finalize`` method.
  6. """
  7. from __future__ import absolute_import
  8. import itertools
  9. import sys
  10. from weakref import ref
  11. __all__ = ["weakref_finalize"]
  12. class weakref_finalize(object):
  13. """Class for finalization of weakrefable objects
  14. finalize(obj, func, *args, **kwargs) returns a callable finalizer
  15. object which will be called when obj is garbage collected. The
  16. first time the finalizer is called it evaluates func(*arg, **kwargs)
  17. and returns the result. After this the finalizer is dead, and
  18. calling it just returns None.
  19. When the program exits any remaining finalizers for which the
  20. atexit attribute is true will be run in reverse order of creation.
  21. By default atexit is true.
  22. """
  23. # Finalizer objects don't have any state of their own. They are
  24. # just used as keys to lookup _Info objects in the registry. This
  25. # ensures that they cannot be part of a ref-cycle.
  26. __slots__ = ()
  27. _registry = {}
  28. _shutdown = False
  29. _index_iter = itertools.count()
  30. _dirty = False
  31. _registered_with_atexit = False
  32. class _Info(object):
  33. __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
  34. def __init__(self, obj, func, *args, **kwargs):
  35. if not self._registered_with_atexit:
  36. # We may register the exit function more than once because
  37. # of a thread race, but that is harmless
  38. import atexit
  39. atexit.register(self._exitfunc)
  40. weakref_finalize._registered_with_atexit = True
  41. info = self._Info()
  42. info.weakref = ref(obj, self)
  43. info.func = func
  44. info.args = args
  45. info.kwargs = kwargs or None
  46. info.atexit = True
  47. info.index = next(self._index_iter)
  48. self._registry[self] = info
  49. weakref_finalize._dirty = True
  50. def __call__(self, _=None):
  51. """If alive then mark as dead and return func(*args, **kwargs);
  52. otherwise return None"""
  53. info = self._registry.pop(self, None)
  54. if info and not self._shutdown:
  55. return info.func(*info.args, **(info.kwargs or {}))
  56. def detach(self):
  57. """If alive then mark as dead and return (obj, func, args, kwargs);
  58. otherwise return None"""
  59. info = self._registry.get(self)
  60. obj = info and info.weakref()
  61. if obj is not None and self._registry.pop(self, None):
  62. return (obj, info.func, info.args, info.kwargs or {})
  63. def peek(self):
  64. """If alive then return (obj, func, args, kwargs);
  65. otherwise return None"""
  66. info = self._registry.get(self)
  67. obj = info and info.weakref()
  68. if obj is not None:
  69. return (obj, info.func, info.args, info.kwargs or {})
  70. @property
  71. def alive(self):
  72. """Whether finalizer is alive"""
  73. return self in self._registry
  74. @property
  75. def atexit(self):
  76. """Whether finalizer should be called at exit"""
  77. info = self._registry.get(self)
  78. return bool(info) and info.atexit
  79. @atexit.setter
  80. def atexit(self, value):
  81. info = self._registry.get(self)
  82. if info:
  83. info.atexit = bool(value)
  84. def __repr__(self):
  85. info = self._registry.get(self)
  86. obj = info and info.weakref()
  87. if obj is None:
  88. return "<%s object at %#x; dead>" % (type(self).__name__, id(self))
  89. else:
  90. return "<%s object at %#x; for %r at %#x>" % (
  91. type(self).__name__,
  92. id(self),
  93. type(obj).__name__,
  94. id(obj),
  95. )
  96. @classmethod
  97. def _select_for_exit(cls):
  98. # Return live finalizers marked for exit, oldest first
  99. L = [(f, i) for (f, i) in cls._registry.items() if i.atexit]
  100. L.sort(key=lambda item: item[1].index)
  101. return [f for (f, i) in L]
  102. @classmethod
  103. def _exitfunc(cls):
  104. # At shutdown invoke finalizers for which atexit is true.
  105. # This is called once all other non-daemonic threads have been
  106. # joined.
  107. reenable_gc = False
  108. try:
  109. if cls._registry:
  110. import gc
  111. if gc.isenabled():
  112. reenable_gc = True
  113. gc.disable()
  114. pending = None
  115. while True:
  116. if pending is None or weakref_finalize._dirty:
  117. pending = cls._select_for_exit()
  118. weakref_finalize._dirty = False
  119. if not pending:
  120. break
  121. f = pending.pop()
  122. try:
  123. # gc is disabled, so (assuming no daemonic
  124. # threads) the following is the only line in
  125. # this function which might trigger creation
  126. # of a new finalizer
  127. f()
  128. except Exception:
  129. sys.excepthook(*sys.exc_info())
  130. assert f not in cls._registry
  131. finally:
  132. # prevent any more finalizers from executing during shutdown
  133. weakref_finalize._shutdown = True
  134. if reenable_gc:
  135. gc.enable()