import gc from types import FrameType def print_cycles(objects, outstream=sys.stdout, show_progress=False): """ objects: A list of objects to find cycles in. It is often useful to pass in gc.garbage to find the cycles that are preventing some objects from being garbage collected. outstream: The stream for output. show_progress: If True, print the number of objects reached as they are found. """ def print_path(path): for i, step in enumerate(path): # next "wraps around" next = path[(i + 1) % len(path)] outstream.write(" %s -- " % str(type(step))) if isinstance(step, dict): for key, val in step.items(): if val is next: outstream.write("[%s]" % repr(key)) break if key is next: outstream.write("[key] = %s" % repr(val)) break elif isinstance(step, list): outstream.write("[%d]" % step.index(next)) elif isinstance(step, tuple): outstream.write("[%d]" % list(step).index(next)) else: outstream.write(repr(step)) outstream.write(" ->\n") outstream.write("\n") def recurse(obj, start, all, current_path): if show_progress: outstream.write("%d\r" % len(all)) all[id(obj)] = None referents = gc.get_referents(obj) for referent in referents: # If we've found our way back to the start, this is # a cycle, so print it out if referent is start: print_path(current_path) # Don't go back through the original list of objects, or # through temporary references to the object, since those # are just an artifact of the cycle detector itself. elif referent is objects or isinstance(referent, FrameType): continue # We haven't seen this object before, so recurse elif id(referent) not in all: recurse(referent, start, all, current_path + [obj]) for obj in objects: outstream.write("Examining: %r\n" % obj) recurse(obj, obj, { }, [])