Example #1
0
def _show_graph(objs,
                edge_func,
                swap_source_target,
                max_depth=3,
                extra_ignore=(),
                filter=None,
                too_many=10,
                highlight=None,
                filename=None,
                extra_info=None,
                refcounts=False,
                shortnames=True,
                output=None,
                cull_func=None):
    if not _isinstance(objs, (list, tuple)):
        objs = [objs]

    is_interactive = False
    if filename and output:
        raise ValueError('Cannot specify both output and filename.')
    elif output:
        f = output
    elif filename and filename.endswith('.dot'):
        f = codecs.open(filename, 'w', encoding='utf-8')
        dot_filename = filename
    elif IS_INTERACTIVE:
        is_interactive = True
        f = StringIO()
    else:
        fd, dot_filename = tempfile.mkstemp(prefix='objgraph-',
                                            suffix='.dot',
                                            text=True)
        f = os.fdopen(fd, "w")
        if getattr(f, 'encoding', None):
            # Python 3 will wrap the file in the user's preferred encoding
            # Re-wrap it for utf-8
            import io
            f = io.TextIOWrapper(f.detach(), 'utf-8')
    f.write('digraph ObjectGraph {\n'
            '  node[shape=box, style=filled, fillcolor=white];\n')
    queue = []
    depth = {}
    ignore = set(extra_ignore)
    ignore.add(id(objs))
    ignore.add(id(extra_ignore))
    ignore.add(id(queue))
    ignore.add(id(depth))
    ignore.add(id(ignore))
    ignore.add(id(sys._getframe()))  # this function
    ignore.add(id(sys._getframe().f_locals))
    ignore.add(id(sys._getframe(1)))  # show_refs/show_backrefs
    ignore.add(id(sys._getframe(1).f_locals))
    for obj in objs:
        f.write('  %s[fontcolor=red];\n' % (_obj_node_id(obj)))
        depth[id(obj)] = 0
        queue.append(obj)
        del obj
    gc.collect()
    nodes = 0
    while queue:
        nodes += 1
        # The names "source" and "target" are reversed here because
        # originally there was just show_backrefs() and we were
        # traversing the reference graph backwards.
        target = queue.pop(0)
        tdepth = depth[id(target)]
        f.write('  %s[label="%s"];\n' %
                (_obj_node_id(target),
                 _obj_label(target, extra_info, refcounts, shortnames)))
        h, s, v = _gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
        if inspect.ismodule(target):
            h = .3
            s = 1
        if highlight and highlight(target):
            h = .6
            s = .6
            v = 0.5 + v * 0.5
        f.write('  %s[fillcolor="%g,%g,%g"];\n' %
                (_obj_node_id(target), h, s, v))
        if v < 0.5:
            f.write('  %s[fontcolor=white];\n' % (_obj_node_id(target)))
        if hasattr(getattr(target, '__class__', None), '__del__'):
            f.write('  %s->%s_has_a_del[color=red,style=dotted,'
                    'len=0.25,weight=10];\n' %
                    (_obj_node_id(target), _obj_node_id(target)))
            f.write('  %s_has_a_del[label="__del__",shape=doublecircle,'
                    'height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];\n' %
                    (_obj_node_id(target)))
        if tdepth >= max_depth:
            continue
        if cull_func is not None and cull_func(target):
            continue
        neighbours = edge_func(target)
        ignore.add(id(neighbours))
        n = 0
        skipped = 0
        for source in neighbours:
            if id(source) in ignore:
                continue
            if filter and not filter(source):
                continue
            if n >= too_many:
                skipped += 1
                continue
            if swap_source_target:
                srcnode, tgtnode = target, source
            else:
                srcnode, tgtnode = source, target
            elabel = _edge_label(srcnode, tgtnode, shortnames)
            f.write('  %s -> %s%s;\n' %
                    (_obj_node_id(srcnode), _obj_node_id(tgtnode), elabel))
            if id(source) not in depth:
                depth[id(source)] = tdepth + 1
                queue.append(source)
            n += 1
            del source
        del neighbours
        if skipped > 0:
            h, s, v = _gradient((0, 1, 1), (0, 1, .3), tdepth + 1, max_depth)
            if swap_source_target:
                label = "%d more references" % skipped
                edge = "%s->too_many_%s" % (_obj_node_id(target),
                                            _obj_node_id(target))
            else:
                label = "%d more backreferences" % skipped
                edge = "too_many_%s->%s" % (_obj_node_id(target),
                                            _obj_node_id(target))
            f.write('  %s[color=red,style=dotted,len=0.25,weight=10];\n' %
                    edge)
            f.write('  too_many_%s[label="%s",shape=box,height=0.25,'
                    'color=red,fillcolor="%g,%g,%g",fontsize=6];\n' %
                    (_obj_node_id(target), label, h, s, v))
            f.write('  too_many_%s[fontcolor=white];\n' %
                    (_obj_node_id(target)))
    f.write("}\n")

    if output:
        return

    if is_interactive:
        return graphviz.Source(f.getvalue())
    else:
        # The file should only be closed if this function was in charge of
        # opening the file.
        f.close()
        print("Graph written to %s (%d nodes)" % (dot_filename, nodes))
        _present_graph(dot_filename, filename)
Example #2
0
def _show_graph(objs, edge_func, swap_source_target,
                max_depth=3, extra_ignore=(), filter=None, too_many=10,
                highlight=None, filename=None, extra_info=None,
                refcounts=False, shortnames=True, output=None,
                cull_func=None):
    if not _isinstance(objs, (list, tuple)):
        objs = [objs]

    is_interactive = False
    if filename and output:
        raise ValueError('Cannot specify both output and filename.')
    elif output:
        f = output
    elif filename and filename.endswith('.dot'):
        f = codecs.open(filename, 'w', encoding='utf-8')
        dot_filename = filename
    elif IS_INTERACTIVE:
        is_interactive = True
        f = StringIO()
    else:
        fd, dot_filename = tempfile.mkstemp(prefix='objgraph-',
                                            suffix='.dot', text=True)
        f = os.fdopen(fd, "w")
        if getattr(f, 'encoding', None):
            # Python 3 will wrap the file in the user's preferred encoding
            # Re-wrap it for utf-8
            import io
            f = io.TextIOWrapper(f.detach(), 'utf-8')
    f.write('digraph ObjectGraph {\n'
            '  node[shape=box, style=filled, fillcolor=white];\n')
    queue = []
    depth = {}
    ignore = set(extra_ignore)
    ignore.add(id(objs))
    ignore.add(id(extra_ignore))
    ignore.add(id(queue))
    ignore.add(id(depth))
    ignore.add(id(ignore))
    ignore.add(id(sys._getframe()))   # this function
    ignore.add(id(sys._getframe().f_locals))
    ignore.add(id(sys._getframe(1)))  # show_refs/show_backrefs
    ignore.add(id(sys._getframe(1).f_locals))
    for obj in objs:
        f.write('  %s[fontcolor=red];\n' % (_obj_node_id(obj)))
        depth[id(obj)] = 0
        queue.append(obj)
        del obj
    gc.collect()
    nodes = 0
    while queue:
        nodes += 1
        # The names "source" and "target" are reversed here because
        # originally there was just show_backrefs() and we were
        # traversing the reference graph backwards.
        target = queue.pop(0)
        tdepth = depth[id(target)]
        f.write('  %s[label="%s"];\n' % (_obj_node_id(target),
                                         _obj_label(target, extra_info,
                                                    refcounts, shortnames)))
        h, s, v = _gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
        if inspect.ismodule(target):
            h = .3
            s = 1
        if highlight and highlight(target):
            h = .6
            s = .6
            v = 0.5 + v * 0.5
        f.write('  %s[fillcolor="%g,%g,%g"];\n'
                % (_obj_node_id(target), h, s, v))
        if v < 0.5:
            f.write('  %s[fontcolor=white];\n' % (_obj_node_id(target)))
        if hasattr(getattr(target, '__class__', None), '__del__'):
            f.write('  %s->%s_has_a_del[color=red,style=dotted,'
                    'len=0.25,weight=10];\n' % (_obj_node_id(target),
                                                _obj_node_id(target)))
            f.write('  %s_has_a_del[label="__del__",shape=doublecircle,'
                    'height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];\n'
                    % (_obj_node_id(target)))
        if tdepth >= max_depth:
            continue
        if cull_func is not None and cull_func(target):
            continue
        neighbours = edge_func(target)
        ignore.add(id(neighbours))
        n = 0
        skipped = 0
        for source in neighbours:
            if id(source) in ignore:
                continue
            if filter and not filter(source):
                continue
            if n >= too_many:
                skipped += 1
                continue
            if swap_source_target:
                srcnode, tgtnode = target, source
            else:
                srcnode, tgtnode = source, target
            elabel = _edge_label(srcnode, tgtnode, shortnames)
            f.write('  %s -> %s%s;\n' % (_obj_node_id(srcnode),
                                         _obj_node_id(tgtnode), elabel))
            if id(source) not in depth:
                depth[id(source)] = tdepth + 1
                queue.append(source)
            n += 1
            del source
        del neighbours
        if skipped > 0:
            h, s, v = _gradient((0, 1, 1), (0, 1, .3), tdepth + 1, max_depth)
            if swap_source_target:
                label = "%d more references" % skipped
                edge = "%s->too_many_%s" % (_obj_node_id(target),
                                            _obj_node_id(target))
            else:
                label = "%d more backreferences" % skipped
                edge = "too_many_%s->%s" % (_obj_node_id(target),
                                            _obj_node_id(target))
            f.write('  %s[color=red,style=dotted,len=0.25,weight=10];\n'
                    % edge)
            f.write('  too_many_%s[label="%s",shape=box,height=0.25,'
                    'color=red,fillcolor="%g,%g,%g",fontsize=6];\n'
                    % (_obj_node_id(target), label, h, s, v))
            f.write('  too_many_%s[fontcolor=white];\n'
                    % (_obj_node_id(target)))
    f.write("}\n")

    if output:
        return

    if is_interactive:
        return graphviz.Source(f.getvalue())
    else:
        # The file should only be closed if this function was in charge of
        # opening the file.
        f.close()
        print("Graph written to %s (%d nodes)" % (dot_filename, nodes))
        _present_graph(dot_filename, filename)