PageRenderTime 36ms CodeModel.GetById 2ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

/dozer/reftree.py

https://bitbucket.org/bbangert/dozer/
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