trackref.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. """This module provides some functions and classes to record and report
  2. references to live object instances.
  3. If you want live objects for a particular class to be tracked, you only have to
  4. subclass from object_ref (instead of object).
  5. About performance: This library has a minimal performance impact when enabled,
  6. and no performance penalty at all when disabled (as object_ref becomes just an
  7. alias to object in that case).
  8. """
  9. from __future__ import print_function
  10. import weakref
  11. from time import time
  12. from operator import itemgetter
  13. from collections import defaultdict
  14. import six
  15. NoneType = type(None)
  16. live_refs = defaultdict(weakref.WeakKeyDictionary)
  17. class object_ref(object):
  18. """Inherit from this class (instead of object) to a keep a record of live
  19. instances"""
  20. __slots__ = ()
  21. def __new__(cls, *args, **kwargs):
  22. obj = object.__new__(cls)
  23. live_refs[cls][obj] = time()
  24. return obj
  25. def format_live_refs(ignore=NoneType):
  26. """Return a tabular representation of tracked objects"""
  27. s = "Live References\n\n"
  28. now = time()
  29. for cls, wdict in sorted(six.iteritems(live_refs),
  30. key=lambda x: x[0].__name__):
  31. if not wdict:
  32. continue
  33. if issubclass(cls, ignore):
  34. continue
  35. oldest = min(six.itervalues(wdict))
  36. s += "%-30s %6d oldest: %ds ago\n" % (
  37. cls.__name__, len(wdict), now - oldest
  38. )
  39. return s
  40. def print_live_refs(*a, **kw):
  41. """Print tracked objects"""
  42. print(format_live_refs(*a, **kw))
  43. def get_oldest(class_name):
  44. """Get the oldest object for a specific class name"""
  45. for cls, wdict in six.iteritems(live_refs):
  46. if cls.__name__ == class_name:
  47. if not wdict:
  48. break
  49. return min(six.iteritems(wdict), key=itemgetter(1))[0]
  50. def iter_all(class_name):
  51. """Iterate over all objects of the same class by its class name"""
  52. for cls, wdict in six.iteritems(live_refs):
  53. if cls.__name__ == class_name:
  54. return six.iterkeys(wdict)