PageRenderTime 97ms CodeModel.GetById 69ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/circuits/web/apps/memorymonitor/reftree.py

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