/dozer/reftree.py
Python | 190 lines | 139 code | 44 blank | 7 comment | 51 complexity | 467e520c9973c50cc98a42efb5dab215 MD5 | raw file
1import gc 2import sys 3 4from types import FrameType 5 6 7class Tree(object): 8 9 def __init__(self, obj, req): 10 self.req = req 11 self.obj = obj 12 self.filename = sys._getframe().f_code.co_filename 13 self._ignore = {} 14 15 def ignore(self, *objects): 16 for obj in objects: 17 self._ignore[id(obj)] = None 18 19 def ignore_caller(self): 20 f = sys._getframe() # = this function 21 cur = f.f_back # = the function that called us (probably 'walk') 22 self.ignore(cur, cur.f_builtins, cur.f_locals, cur.f_globals) 23 caller = f.f_back # = the 'real' caller 24 self.ignore(caller, caller.f_builtins, caller.f_locals, caller.f_globals) 25 26 def walk(self, maxresults=100, maxdepth=None): 27 """Walk the object tree, ignoring duplicates and circular refs.""" 28 self.seen = {} 29 self.ignore(self, self.__dict__, self.obj, self.seen, self._ignore) 30 31 # Ignore the calling frame, its builtins, globals and locals 32 self.ignore_caller() 33 34 self.maxdepth = maxdepth 35 count = 0 36 for result in self._gen(self.obj): 37 yield result 38 count += 1 39 if maxresults and count >= maxresults: 40 yield 0, 0, "==== Max results reached ====" 41 raise StopIteration 42 43 def print_tree(self, maxresults=100, maxdepth=None): 44 """Walk the object tree, pretty-printing each branch.""" 45 self.ignore_caller() 46 for depth, refid, rep in self.walk(maxresults, maxdepth): 47 print ("%9d" % refid), (" " * depth * 2), rep 48 49 50def _repr_container(obj): 51 return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj) 52repr_dict = _repr_container 53repr_set = _repr_container 54repr_list = _repr_container 55repr_tuple = _repr_container 56 57def repr_str(obj): 58 return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj) 59repr_unicode = repr_str 60 61def repr_frame(obj): 62 return "frame from %s line %s" % (obj.f_code.co_filename, obj.f_lineno) 63 64def get_repr(obj, limit=250): 65 typename = getattr(type(obj), "__name__", None) 66 handler = globals().get("repr_%s" % typename, repr) 67 68 try: 69 result = handler(obj) 70 except: 71 result = "unrepresentable object: %r" % sys.exc_info()[1] 72 73 if len(result) > limit: 74 result = result[:limit] + "..." 75 76 return result 77 78 79class ReferentTree(Tree): 80 81 def _gen(self, obj, depth=0): 82 if self.maxdepth and depth >= self.maxdepth: 83 yield depth, 0, "---- Max depth reached ----" 84 raise StopIteration 85 86 for ref in gc.get_referents(obj): 87 if id(ref) in self._ignore: 88 continue 89 elif id(ref) in self.seen: 90 yield depth, id(ref), "!" + get_repr(ref) 91 continue 92 else: 93 self.seen[id(ref)] = None 94 yield depth, id(ref), get_repr(ref) 95 96 for child in self._gen(ref, depth + 1): 97 yield child 98 99 100class ReferrerTree(Tree): 101 102 def _gen(self, obj, depth=0): 103 if self.maxdepth and depth >= self.maxdepth: 104 yield depth, 0, "---- Max depth reached ----" 105 raise StopIteration 106 107 refs = gc.get_referrers(obj) 108 refiter = iter(refs) 109 self.ignore(refs, refiter) 110 for ref in refiter: 111 # Exclude all frames that are from this module. 112 if isinstance(ref, FrameType): 113 if ref.f_code.co_filename == self.filename: 114 continue 115 116 if id(ref) in self._ignore: 117 continue 118 elif id(ref) in self.seen: 119 yield depth, id(ref), "!" + get_repr(ref) 120 continue 121 else: 122 self.seen[id(ref)] = None 123 yield depth, id(ref), get_repr(ref) 124 125 for parent in self._gen(ref, depth + 1): 126 yield parent 127 128 129 130class CircularReferents(Tree): 131 132 def walk(self, maxresults=100, maxdepth=None): 133 """Walk the object tree, showing circular referents.""" 134 self.stops = 0 135 self.seen = {} 136 self.ignore(self, self.__dict__, self.seen, self._ignore) 137 138 # Ignore the calling frame, its builtins, globals and locals 139 self.ignore_caller() 140 141 self.maxdepth = maxdepth 142 count = 0 143 for result in self._gen(self.obj): 144 yield result 145 count += 1 146 if maxresults and count >= maxresults: 147 yield 0, 0, "==== Max results reached ====" 148 raise StopIteration 149 150 def _gen(self, obj, depth=0, trail=None): 151 if self.maxdepth and depth >= self.maxdepth: 152 self.stops += 1 153 raise StopIteration 154 155 if trail is None: 156 trail = [] 157 158 for ref in gc.get_referents(obj): 159 if id(ref) in self._ignore: 160 continue 161 elif id(ref) in self.seen: 162 continue 163 else: 164 self.seen[id(ref)] = None 165 166 refrepr = get_repr(ref) 167 if id(ref) == id(self.obj): 168 yield trail + [refrepr,] 169 170 for child in self._gen(ref, depth + 1, trail + [refrepr,]): 171 yield child 172 173 def print_tree(self, maxresults=100, maxdepth=None): 174 """Walk the object tree, pretty-printing each branch.""" 175 self.ignore_caller() 176 for trail in self.walk(maxresults, maxdepth): 177 print trail 178 if self.stops: 179 print "%s paths stopped because max depth reached" % self.stops 180 181 182def count_objects(): 183 d = {} 184 for obj in gc.get_objects(): 185 objtype = type(obj) 186 d[objtype] = d.get(objtype, 0) + 1 187 d = [(v, k) for k, v in d.iteritems()] 188 d.sort() 189 return d 190