def test_render_explicit_ipython_format_inside_ipython(): graph = nx.Graph() graph.add_edge("A", "B") for format in ["svg", "png", "raw"]: output = nxv.render(graph, format=format) with assert_ipython_display_call(output, f"ipython/{format}"): nxv.render(graph, format=f"ipython/{format}")
def test_render_invalid(): graph = nx.Graph() graph.add_node(0) style = nxv.Style( node={"label": H.table(["x"], attributes={"notanattribute": "y"})}) with pytest.raises(nxv.GraphVizError): nxv.render(graph, style, format="svg")
def test_html_like_labels(): graph = nx.OrderedDiGraph() graph.add_node( 0, label=H.table([ H.table_row([H.table_cell("A", attributes={"COLSPAN": 2})]), H.table_row([H.table_cell("C"), H.table_cell("D")]), ]), ) graph.add_node(1, label=H.bold('3 < 7 "hello" & "world" 5 > 2')) graph.add_node(2, label=H.join(["hello", H.italic("world")])) graph.add_edge(0, 1) style = nxv.Style(node=lambda u, d: d) actual = nxv.render(graph, style, format="raw") expected = textwrap.dedent(""" digraph "G" { graph []; node0000 [label=<<TABLE><TR><TD COLSPAN="2">A</TD></TR><TR><TD>C</TD><TD>D</TD></TR></TABLE>>]; node0001 [label=<<B>3 < 7 "hello" & "world" 5 > 2</B>>]; node0002 [label=<hello<I>world</I>>]; node0000 -> node0001 []; } """).strip() assert actual == expected
def test_render(): graph = nx.DiGraph() nx.add_path(graph, [0, 1, 2, 3]) graph.add_edges_from([("even", 0), ("even", 2), ("odd", 1), ("odd", 3)]) def int_color(n): return "gray" if n % 2 == 0 else "darkred" style = nxv.Style( graph={ "label": "Beware the odd numbers!", "labelloc": "t" }, node=nxv.switch( lambda u, d: type(u), { str: { "shape": "diamond" }, int: lambda u, d: { "shape": "circle", "style": "filled", "color": int_color(u), "fontcolor": "white", }, }, ), edge=nxv.switch( lambda u, v, d: (type(u), type(v)), { (str, int): lambda u, v, d: { "color": int_color(v) }, (int, int): { "dir": "none", "style": "dashed" }, }, ), ) nxv.render(graph, style, format="svg")
def test_render_multi(): graph = nx.MultiDiGraph() graph.add_edge(0, 1, key="LessThan") graph.add_edge(0, 2, key="LessThan") graph.add_edge(1, 2, key="LessThan") graph.add_edge(0, 1, key="Successor") graph.add_edge(1, 2, key="Successor") style = nxv.Style(edge=nxv.switch( lambda u, v, k, d: k, { "LessThan": lambda u, v, k, d: { "color": "red", "label": f"{u} < {v}", }, "Successor": lambda u, v, k, d: { "color": "blue", "label": f"{u} + 1 = {v}", }, }, )) nxv.render(graph, style, format="svg")
def build_logo(): shutil.rmtree("docs/_static/logo", ignore_errors=True) os.makedirs("docs/_static/logo", exist_ok=True) graph = nx.Graph() nx.add_path(graph, [0, 1, 2, 3, 4, 5, 0]) nx.add_star(graph, [6, 0, 1, 2, 3, 4, 5]) style = nxv.Style( graph={ "pad": 1 / 8, "bgcolor": "#00000000", "size": "1,1", "ratio": 1, }, node=lambda u, d: { "shape": "circle", "label": None, "width": 3 / 4, "style": "filled", "fillcolor": "#009AA6" if u % 2 else "#E37222", "penwidth": 5, }, edge={"penwidth": 5}, ) path = "docs/_static/logo/logo.svg" LOGGER.info(f"Rendering {path}") with open(path, "wb") as f: f.write(nxv.render(graph, style, algorithm="neato", format="svg")) for size in [16, 32, 40, 48, 64, 128, 256]: style_with_dpi = nxv.compose([style, nxv.Style(graph={"dpi": size})]) path = f"docs/_static/logo/logo-{size}.png" LOGGER.info(f"Rendering {path}") with open(path, "wb") as f: f.write( nxv.render(graph, style_with_dpi, algorithm="neato", format="png"))
def test_multiline_labels(): graph = nx.OrderedDiGraph() graph.add_node(0, label="A\nB") graph.add_node(1, label="C\nD") graph.add_edge(0, 1) style = nxv.Style(node=lambda u, d: d) actual = nxv.render(graph, style, format="raw") expected = textwrap.dedent(""" digraph "G" { graph []; node0000 [label="A B"]; node0001 [label="C D"]; node0000 -> node0001 []; } """).strip() assert actual == expected
def test_subgraph_indentation(): graph = nx.OrderedDiGraph() graph.add_node("A") graph.add_node("B") graph.add_edge("A", "B") actual = nxv.render(graph, subgraph_func=lambda u, d: u, format="raw") expected = textwrap.dedent(""" digraph "G" { graph []; subgraph "A" { graph []; node0000 [label="A"]; } subgraph "B" { graph []; node0001 [label="B"]; } node0000 -> node0001 []; } """).strip() assert actual == expected
def render(plan: typing.Union[Plan, Graph, typing.Tuple[Plan, typing.Optional[Node]]], *, registry: Registry = None, predicate: typing.Callable[[Node, dict], bool] = None, level: typing.Optional[int] = None, format: typing.Optional[str] = None) -> typing.Optional[bytes]: """ Use :mod:`nxv` to render a plan's symbolic call graph. :param plan: A plan's symbolic call graph. :param registry: A registry to include in the visualization. :param predicate: An optional node predicate ``f(u, d)`` that determines whether a node ``u`` with attribute dict ``d`` will be included in the render. :param level: Optional maximum number of scope levels to view. Nodes are grouped by scope[:level]. :param format: The nxv/GraphViz output format to produce. :return: The rendered graph. """ import nxv if (isinstance(plan, tuple) and len(plan) == 2 and isinstance(plan[0], Plan) and (isinstance(plan[1], Node) or plan[1] is None)): plan = plan[0] validation.assert_is_instance(plan, "plan", (Plan, Graph)) graph = (plan.graph if isinstance(plan, Plan) else plan).copy() if predicate: graph.remove_nodes_from( [u for u, d in graph.nodes(data=True) if not predicate(u, d)]) if level is not None: scope_groups = OrderedDict() for u in graph.nodes(): scope = get_scope(graph, u) if scope: scope_groups.setdefault(scope[:level], []).append(u) for scope, group in scope_groups.items(): group = set(group) predecessors = set() successors = set() for w in group: predecessors.update(graph.predecessors(w)) successors.update( (v, e) for _, v, e in graph.out_edges(w, keys=True)) scope_node = Scope() graph.add_node(scope_node, scope=scope, count=len(group)) graph.remove_nodes_from(group) graph.add_edges_from((predecessor, scope_node, Dependency()) for predecessor in predecessors if predecessor not in group) graph.add_edges_from((scope_node, successor, dependency) for successor, dependency in successors if successor not in group) style = default_style(registry) return nxv.render( graph, style, algorithm="dot", format=format, )
def build_example(example): path = f"docs/_static/example/{example.__name__}.svg" LOGGER.info(f"Rendering {path}") with open(path, "wb") as f: f.write(nxv.render(**example(), format="svg"))
def test_render_strange_characters(): graph = nx.Graph() nx.add_path(graph, range(1000)) style = nxv.Style(node=lambda u, d: {"label": f"{u}:{chr(u)}"}) nxv.render(graph, style, format="svg")
def test_render_default_format_inside_ipython(): graph = nx.Graph() graph.add_edge("A", "B") output = nxv.render(graph, format="svg") with assert_ipython_display_call(output, "ipython/svg"): nxv.render(graph)
def test_render_default_format_outside_ipython(): graph = nx.Graph() with pytest.raises(ValueError): nxv.render(graph)
def test_render_color(): graph = nx.Graph() graph.add_node(0) style = nxv.Style(node={"label": None, "color": (1, 1, 1)}) nxv.render(graph, style, format="svg")