/circuits/web/apps/memorymonitor/reftree.py
https://bitbucket.org/prologic/circuits/ · Python · 188 lines · 138 code · 43 blank · 7 comment · 39 complexity · 1f34d35945ebb3c675303861378515b8 MD5 · raw file
- import gc
- import sys
- from types import FrameType
- class Tree:
-
- def __init__(self, obj):
- self.obj = obj
- self.filename = sys._getframe().f_code.co_filename
- self._ignore = {}
-
- def ignore(self, *objects):
- for obj in objects:
- self._ignore[id(obj)] = None
-
- def ignore_caller(self):
- f = sys._getframe() # = this function
- cur = f.f_back # = the function that called us (probably 'walk')
- self.ignore(cur, cur.f_builtins, cur.f_locals, cur.f_globals)
- caller = f.f_back # = the 'real' caller
- self.ignore(caller, caller.f_builtins, caller.f_locals, caller.f_globals)
-
- def walk(self, maxresults=100, maxdepth=None):
- """Walk the object tree, ignoring duplicates and circular refs."""
- self.seen = {}
- self.ignore(self, self.__dict__, self.obj, self.seen, self._ignore)
-
- # Ignore the calling frame, its builtins, globals and locals
- self.ignore_caller()
-
- self.maxdepth = maxdepth
- count = 0
- for result in self._gen(self.obj):
- yield result
- count += 1
- if maxresults and count >= maxresults:
- yield 0, 0, "==== Max results reached ===="
- raise StopIteration
-
- def print_tree(self, maxresults=100, maxdepth=None):
- """Walk the object tree, pretty-printing each branch."""
- self.ignore_caller()
- for depth, refid, rep in self.walk(maxresults, maxdepth):
- print ("%9d" % refid), (" " * depth * 2), rep
- def _repr_container(obj):
- return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj)
- repr_dict = _repr_container
- repr_set = _repr_container
- repr_list = _repr_container
- repr_tuple = _repr_container
- def repr_str(obj):
- return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj)
- repr_unicode = repr_str
- def repr_frame(obj):
- return "frame from %s line %s" % (obj.f_code.co_filename, obj.f_lineno)
- def get_repr(obj, limit=250):
- typename = getattr(type(obj), "__name__", None)
- handler = globals().get("repr_%s" % typename, repr)
-
- try:
- result = handler(obj)
- except:
- result = "unrepresentable object: %r" % sys.exc_info()[1]
-
- if len(result) > limit:
- result = result[:limit] + "..."
-
- return result
- class ReferentTree(Tree):
-
- def _gen(self, obj, depth=0):
- if self.maxdepth and depth >= self.maxdepth:
- yield depth, 0, "---- Max depth reached ----"
- raise StopIteration
-
- for ref in gc.get_referents(obj):
- if id(ref) in self._ignore:
- continue
- elif id(ref) in self.seen:
- yield depth, id(ref), "!" + get_repr(ref)
- continue
- else:
- self.seen[id(ref)] = None
- yield depth, id(ref), get_repr(ref)
-
- for child in self._gen(ref, depth + 1):
- yield child
- class ReferrerTree(Tree):
-
- def _gen(self, obj, depth=0):
- if self.maxdepth and depth >= self.maxdepth:
- yield depth, 0, "---- Max depth reached ----"
- raise StopIteration
-
- refs = gc.get_referrers(obj)
- refiter = iter(refs)
- self.ignore(refs, refiter)
- for ref in refiter:
- # Exclude all frames that are from this module.
- if isinstance(ref, FrameType):
- if ref.f_code.co_filename == self.filename:
- continue
-
- if id(ref) in self._ignore:
- continue
- elif id(ref) in self.seen:
- yield depth, id(ref), "!" + get_repr(ref)
- continue
- else:
- self.seen[id(ref)] = None
- yield depth, id(ref), get_repr(ref)
-
- for parent in self._gen(ref, depth + 1):
- yield parent
- class CircularReferents(Tree):
-
- def walk(self, maxresults=100, maxdepth=None):
- """Walk the object tree, showing circular referents."""
- self.stops = 0
- self.seen = {}
- self.ignore(self, self.__dict__, self.seen, self._ignore)
-
- # Ignore the calling frame, its builtins, globals and locals
- self.ignore_caller()
-
- self.maxdepth = maxdepth
- count = 0
- for result in self._gen(self.obj):
- yield result
- count += 1
- if maxresults and count >= maxresults:
- yield 0, 0, "==== Max results reached ===="
- raise StopIteration
-
- def _gen(self, obj, depth=0, trail=None):
- if self.maxdepth and depth >= self.maxdepth:
- self.stops += 1
- raise StopIteration
-
- if trail is None:
- trail = []
-
- for ref in gc.get_referents(obj):
- if id(ref) in self._ignore:
- continue
- elif id(ref) in self.seen:
- continue
- else:
- self.seen[id(ref)] = None
-
- refrepr = get_repr(ref)
- if id(ref) == id(self.obj):
- yield trail + [refrepr,]
-
- for child in self._gen(ref, depth + 1, trail + [refrepr,]):
- yield child
-
- def print_tree(self, maxresults=100, maxdepth=None):
- """Walk the object tree, pretty-printing each branch."""
- self.ignore_caller()
- for trail in self.walk(maxresults, maxdepth):
- print trail
- if self.stops:
- print "%s paths stopped because max depth reached" % self.stops
- def count_objects():
- d = {}
- for obj in gc.get_objects():
- objtype = type(obj)
- d[objtype] = d.get(objtype, 0) + 1
- d = [(v, k) for k, v in d.iteritems()]
- d.sort()
- return d