예제 #1
0
def output_parent_function_graph(rule_classification_data_bundle):
    report_dict, reference_dict = rule_classification_data_bundle

    identifier_dict = {
        parent: f"p{index}"
        for index, parent in enumerate(report_dict.keys())
    }

    dot = Digraph(**_GRAPH_SETTINGS)

    for parent, identifier in identifier_dict.items():
        descriptions = "\l".join(report_dict[parent]) + "\l"

        with dot.subgraph(
                name=f"cluster_{identifier}",
                graph_attr={
                    "label": _get_function_display_name(parent),
                    "fontsize": "16",
                },
        ) as sub:
            sub.node(identifier, label=descriptions)

    edge_list = []
    for parent, identifier in identifier_dict.items():
        edge_list.extend([(identifier, identifier_dict[function])
                          for function in reference_dict[parent]])

    dot.edges(edge_list)

    dot.render()
예제 #2
0
    def _visit_blocks(
        self,
        graph: Digraph,
        block: Block,
        visited: Set[Block] = set(),
        calls: bool = True,
        format: str = None,
        interactive: bool = False,
    ) -> None:
        # Don't visit blocks twice.
        if block in visited:
            return
        visited.add(block)
        nodeshape, nodecolor, nodelabel = self.stylize_node(block)
        node_type = block.type()
        original_nodelabel = nodelabel
        nodelabel = ""
        if (self.isShort and not isinstance(node_type, ast.ClassDef)
                and not isinstance(node_type, ast.FunctionDef)
                and not isinstance(node_type, ast.If)
                and not isinstance(node_type, ast.While)):
            sub_pattern = r"""(\"|')                # Group 1: " or '
                              (?=[^\"'\r\n]{20,})   # Enforce min length of 20
                              ([^\"'\r\n]{,20})     # Group 2: Words that stay
                              ([^\"'\r\n]{,9999})   # Group 3: Shorten these 
                              (\"|')"""  # Group 4: " or '
            original_nodelabel = original_nodelabel.replace("\l", "\n")
            for line in original_nodelabel.splitlines():
                tmp_line = re.sub(sub_pattern,
                                  r"\1\2...\4",
                                  line,
                                  flags=re.VERBOSE)
                nodelabel += tmp_line + "\l"
        else:
            nodelabel = original_nodelabel

        graph.node(
            str(block.id),
            label=nodelabel,
            _attributes={
                "style": f"filled,{self.border_style(block, interactive)}",
                "shape": nodeshape,
                "fillcolor": self.fillcolor(block, interactive, nodecolor),
            },
        )

        if isinstance(block, TryBlock):
            for except_block in block.except_blocks.values():
                self._visit_blocks(graph, except_block, visited, calls, format)

        if calls and block.func_calls:
            calls_node = str(block.id) + "_calls"

            # Remove any duplicates by splitting on newlines and creating a set
            calls_label = block.get_calls().strip()

            # Create a new subgraph for call statement
            calls_subgraph = gv.Digraph(
                name=f"cluster_{block.id}",
                format=format,
                graph_attr={
                    "rankdir": "TB",
                    "ranksep": "0.02",
                    "style": "filled",
                    "color": self.fillcolor(block, interactive, "purple"),
                    "compound": "true",
                    "fontname": "DejaVu Sans Mono",
                    "shape": self.node_styles[ast.Call][0],
                    "label": "",
                },
                node_attr={"fontname": "DejaVu Sans Mono"},
                edge_attr={"fontname": "DejaVu Sans Mono"},
            )
            # Generate control flow edges for function arguments
            for func_block in block.func_blocks:
                graph.edge(
                    str(block.id),
                    str(func_block.id),
                    label="calls",
                    _attributes={"style": "dashed"},
                )
                self._visit_func(
                    calls_subgraph,
                    func_block,
                    visited=set(),
                    interactive=interactive,
                )
            graph.subgraph(calls_subgraph)

            tmp = ""
            for line in calls_label.splitlines():
                if "input" in line:
                    input_node = str(block.id) + "_input"
                    nodeshape, nodecolor = self.node_styles["input"]
                    graph.node(
                        input_node,
                        label=line,
                        _attributes={
                            "style":
                            f"filled,{self.border_style(block, interactive)}",
                            "shape":
                            nodeshape,
                            "fillcolor":
                            self.fillcolor(block, interactive, nodecolor),
                        },
                    )
                    graph.edge(input_node, str(block.id))  # yellow
                    # _attributes={'style': 'dashed'})

                else:
                    line += "\l"
                    tmp += line

        # Recursively visit all the blocks of the CFG.
        for exit in block.exits:
            assert block == exit.source
            self._visit_blocks(
                graph,
                exit.target,
                visited,
                calls=calls,
                format=format,
                interactive=interactive,
            )
            edgeshape, edgecolor, edgelabel = self.stylize_edge(exit)
            graph.edge(
                str(block.id),
                str(exit.target.id),
                label=edgelabel,
                _attributes={"color": edgecolor},
            )