Brandon Rhodes avatar Brandon Rhodes committed bba68f1

Added basic loop detection.

Comments (0)

Files changed (1)

     def verbose(self):
         return ReprStr(format_object(self, verbose=True))
 
+    @property
+    def loops(self, depth=4):
+        return ReprStr(format_loops(self._obj, depth=4))
+
 
 def get_typename(obj):
     """Compute an object's type without causing side effects."""
     if d is not None:
         for k, v in d.iteritems():
             yield 'a_' + k, v
-    if isinstance(obj, (tuple, list)):
+    if isinstance(obj, tuple):
+        for i in range(tuple.__len__(obj)):
+            yield 'item{}'.format(i), tuple.__getitem__(obj, i)
+    if isinstance(obj, list):
         for i in range(list.__len__(obj)):
-            yield 'item{}'.format(i), list.__getitem__(i)
+            yield 'item{}'.format(i), list.__getitem__(obj, i)
     if isinstance(obj, dict):
         for i, (k, v) in enumerate(dict.iteritems(obj)):
             yield 'key{}'.format(i), k
             yield 'value{}'.format(i), v
     elif isinstance(obj, set):
         for i, k in enumerate(set.iterkeys(obj)):
-            yield 'key{}'.format(i), k
+            yield 'member{}'.format(i), k
 
 
 def format_object(squinter, verbose=False):
     squinter.load()
     items = squinter.refs.items()
     items.sort()
+    if not verbose:
+        t += summarize_items(items)
     for name, value in items:
         t += '\n  {} {}'.format(name, format_summary(value))
     return t
 
 
+def summarize_items(items):
+    """Remove primitively-typed objects from `items` and return a summary."""
+    i = 0
+    counts = {int: 0, float: 0, complex: 0, str: 0, unicode: 0}
+    while i < len(items):
+        k, v = items[i]
+        vtype = type(v)
+        if vtype in counts:
+            if vtype in (str, unicode):
+                counts[vtype] += len(v)
+            else:
+                counts[vtype] += 1
+            del items[i]
+        else:
+            i += 1
+    citems = counts.items()
+    citems.sort()
+    return ''.join('  {}*{}'.format(k.__name__, v) for k, v in citems if v)
+
+
+def format_loops(obj, depth=4):
+    """Describe the object loops found by deliving beneath `obj`."""
+    looplist = []
+    _loop_search(obj, (), ['$'], depth, looplist)
+    return '\n'.join('{} <- .{}'.format(*t) for t in looplist)
+
+
+def _loop_search(obj, parent_ids, names, depth, looplist):
+    parent_ids += (id(obj),)
+    go_deeper = depth > 1
+    for name, value in iter_refs(obj):
+        names.append(name)
+        if id(value) in parent_ids:
+            i = parent_ids.index(id(value)) + 1
+            looplist.append(('.'.join(names[:i]), '.'.join(names[i:])))
+        elif go_deeper:
+            _loop_search(value, parent_ids, names, depth - 1, looplist)
+        names.pop()
+
+
 class ReprStr(str):
     """A string whose repr() lacks quotation marks and escaping."""
     def __repr__(self):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.