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 quickstart_graph_style(graph): # BEGIN EXAMPLE style = nxv.Style( graph={"rankdir": "LR"}, node={"shape": "square"}, edge={"style": "dashed"}, ) # END EXAMPLE return dict(graph=graph, style=style)
def quickstart_graph_functional_style(graph): # BEGIN EXAMPLE style = nxv.Style( graph={"rankdir": "LR"}, node=lambda u, d: {"shape": "circle" if u in "AEIOU" else "square"}, edge=lambda u, v, d: { "style": "dashed", "label": u + v }, ) # END EXAMPLE return dict(graph=graph, style=style)
def networkx_plus_graphviz(): # BEGIN EXAMPLE graph = nx.DiGraph() graph.add_edge("NetworkX", "+") graph.add_edge("GraphViz", "+") graph.add_edge("+", "nxv") style = nxv.Style( node=lambda u, d: {"shape": "circle" if u == "+" else "box"}, ) style = nxv.compose([style, nxv.styles.font(fontname="Lato")]) # END EXAMPLE return dict(graph=graph, style=style)
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_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 default_style(registry: Registry = None): import nxv if registry is None: registry = Registry() style = nxv.Style( node=nxv.chain([ { "shape": "box", "style": "filled", "margin": 0.05, "width": 0, "height": 0, "fontcolor": "white", }, nxv.switch( lambda u, d: type(u), { Literal: { "fillcolor": TEAL }, Call: { "fillcolor": ORANGE }, Scope: { "fillcolor": GRAY }, }, ), nxv.switch(lambda u, d: u in registry, { True: { "fillcolor": PURPLE }, False: {} }), nxv.switch( lambda u, d: u in registry, { False: nxv.switch( lambda u, d: type(u), { Literal: lambda u, d: { "label": compact_repr(u.value) }, Call: lambda u, d: { "label": fully_qualified_name(u.fn) }, Scope: lambda u, d: { "label": "\n".join([ *(str(value) for value in d["scope"]), "{} {}".format( d["count"], "node" if d["count"] == 1 else "nodes", ), ]) }, }, ), True: nxv.switch( lambda u, d: type(u), { Literal: lambda u, d: { "label": "\n".join( [compact_repr(u.value), repr(registry[u])]) }, Call: lambda u, d: { "label": "\n".join([ fully_qualified_name(u.fn), repr(registry[u]) ]) }, }, ), }, ), ]), edge=nxv.chain([ { "arrowhead": "open", "arrowtail": "open" }, nxv.switch( lambda u, v, e, d: type(e), { Dependency: { "style": "dashed" }, PositionalArg: lambda u, v, e, d: { "label": e.index }, KeywordArg: lambda u, v, e, d: { "label": e.name }, }, ), ]), ) style = nxv.compose([style, nxv.styles.font("Courier", 10)]) return style
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_color(): graph = nx.Graph() graph.add_node(0) style = nxv.Style(node={"label": None, "color": (1, 1, 1)}) nxv.render(graph, style, format="svg")