--- a/Lib/weakref.py Sat Jul 28 17:45:28 2012 +0100 +++ b/Lib/weakref.py Wed Aug 01 16:40:41 2012 +0100 @@ -21,13 +21,15 @@ from _weakrefset import WeakSet, _IterationGuard import collections # Import after _weakref to avoid circular import. +import sys +import atexit ProxyTypes = (ProxyType, CallableProxyType) __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", "WeakKeyDictionary", "ReferenceType", "ProxyType", "CallableProxyType", "ProxyTypes", "WeakValueDictionary", - "WeakSet"] + "WeakSet", "finalize"] class WeakValueDictionary(collections.MutableMapping): @@ -383,3 +385,91 @@ d[ref(key, self._remove)] = value if len(kwargs): self.update(kwargs) + + +class finalize(object): + """Class for finalization of weakrefable objects + + finalize(obj, func, *args, **kwds) returns a callable finalizer + object which will be called when obj is garbage collected. The + first time the finalizer is called it evaluates func(*arg, **kwds) + and returns the result. After this the finalizer is dead, and + calling it just returns None. + + When the program exits any remaining finalizers for which the + atexit attribute is true will be run in reverse order of creation. + By default atexit is false. + """ + __slots__ = ("atexit", "_count") + _registry = {} + _shutdown = False + _class_count = 0 + + def __init__(self, obj, func, *args, **kwds): + self.atexit = False + self._count = finalize._class_count + finalize._class_count += 1 + if obj is None: + wr = None + else: + wr = ref(obj, self._make_wrapper()) + self._registry[self] = (wr, func, args, kwds) + + def __call__(self): + """If the finalizer is dead return None. Otherwise, mark it as dead + and return the result of calling func(*args, **kwds). + """ + tmp = self._registry.pop(self, None) + if tmp and not self._shutdown and self._check(): + wr, func, args, kwds = tmp + return func(*args, **kwds) + + def pop(self): + """If the finalizer is dead return None. Otherwise, mark it as dead + and return the tuple (wr, func, args, kwds) where wr is a weak + reference to obj. + """ + return self._registry.pop(self, None) + + def get(self): + """If the finalizer is dead return None. Otherwise, return the tuple + (wr, func, args, kwds) where wr is a weak reference to obj. + """ + return self._registry.get(self, None) + + def _make_wrapper(self): + # Return a wrapper for self which can be used as weakref callback + def wrapper(wr): + try: + self() + except: + # Print traceback with first two frames stripped + sys.stderr.write("Ignored exception in finalizer - ") + t, v, tb = sys.exc_info() + tb = tb and tb.tb_next and tb.tb_next.tb_next + v.__traceback__ = tb + sys.excepthook(t, v, tb) + return wrapper + + def _sortkey(self): + # Used by _atexit_callback() - overridable by subclasses + return (0, self._count) + + def _check(self): + # Overridable by subclasses + return True + + @classmethod + def _atexit_callback(cls): + # At shutdown invoke finalizers for which atexit is true + try: + L = [f for f in cls._registry.keys() if f.atexit] + L.sort(key=lambda obj : obj._sortkey(), reverse=True) + for f in L: + f._make_wrapper()(None) + finally: + # prevent any more finalizers from executing during shutdown + cls._shutdown = True + cls._registry.clear() + +atexit.register(finalize._atexit_callback)