"""This module provides some functions and classes to record and report references to live object instances. If you want live objects for a particular class to be tracked, you only have to subclass from object_ref (instead of object). About performance: This library has a minimal performance impact when enabled, and no performance penalty at all when disabled (as object_ref becomes just an alias to object in that case). """ from __future__ import print_function import weakref from time import time from operator import itemgetter from collections import defaultdict import six NoneType = type(None) live_refs = defaultdict(weakref.WeakKeyDictionary) class object_ref(object): """Inherit from this class (instead of object) to a keep a record of live instances""" __slots__ = () def __new__(cls, *args, **kwargs): obj = object.__new__(cls) live_refs[cls][obj] = time() return obj def format_live_refs(ignore=NoneType): """Return a tabular representation of tracked objects""" s = "Live References\n\n" now = time() for cls, wdict in sorted(six.iteritems(live_refs), key=lambda x: x[0].__name__): if not wdict: continue if issubclass(cls, ignore): continue oldest = min(six.itervalues(wdict)) s += "%-30s %6d oldest: %ds ago\n" % ( cls.__name__, len(wdict), now - oldest ) return s def print_live_refs(*a, **kw): """Print tracked objects""" print(format_live_refs(*a, **kw)) def get_oldest(class_name): """Get the oldest object for a specific class name""" for cls, wdict in six.iteritems(live_refs): if cls.__name__ == class_name: if not wdict: break return min(six.iteritems(wdict), key=itemgetter(1))[0] def iter_all(class_name): """Iterate over all objects of the same class by its class name""" for cls, wdict in six.iteritems(live_refs): if cls.__name__ == class_name: return six.iterkeys(wdict)