import os as _os def convey(x, fs): y = x for f in fs: y = f(y) return y def string_coin(template, arguments, options = None): options = ( { "open": "{{", "close": "}}", } | (options or {}) ) result = template for (key, value, ) in arguments.items(): if (value is None): pass else: result = result.replace( ( "%s%s%s" % ( options["open"], key, options["close"], ) ), value ) return result def file_read(path): handle = open(path, "r") content = handle.read() handle.close() return content def file_write(path, content): _os.makedirs(_os.path.dirname(path), exist_ok = True) handle = open(path, "w" if _os.path.exists(path) else "a") handle.write(content) handle.close() return None def dot_entry(head, attributes, options = None): options = ( { "indentation": 0, } | (options or {}) ) return string_coin( "{{indentation}}{{head}} [{{attributes}}];\n", { "indentation": ("\t" * options["indentation"]), "head": head, "attributes": convey( attributes, [ lambda x: x.items(), lambda x: map( lambda pair: string_coin( "{{key}}=\"{{value}}\"", { "key": pair[0], "value": pair[1] } ), x ), ", ".join, ] ), } ) def dot_node(node, options = None): options = ( { "indentation": 0, } | (options or {}) ) return dot_entry( string_coin("{{name}}", {"name": node["name"]}), node["attributes"], options ) def dot_edge(edge, options = None): options = ( { "indentation": 0, } | (options or {}) ) return dot_entry( string_coin("{{name_from}} -> {{name_to}}", {"name_from": edge["name_from"], "name_to": edge["name_to"]}), edge["attributes"], options ) def dot_subgraph(subgraph, options = None): options = ( { "indentation": 0, } | (options or {}) ) return string_coin( ( "{{indentation}}subgraph cluster_{{name}}\n" + "{{indentation}}{\n" + "{{attributes}}\n" + "{{nodes}}\n" + "{{edges}}\n" + "{{indentation}}}\n" ), { "indentation": ("\t" * options["indentation"]), "name": subgraph["name"], "attributes": convey( subgraph["attributes"], [ lambda x: x.items(), lambda x: map( lambda pair: string_coin( "{{indentation}}{{key}}=\"{{value}}\";\n", { "indentation": ("\t" * (options["indentation"] + 1)), "key": pair[0], "value": pair[1] } ), x ), "".join, ] ), "nodes": "".join(map(lambda node: dot_node(node, {"indentation": (options["indentation"] + 1)}), subgraph["nodes"])), "edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": (options["indentation"] + 1)}), subgraph["edges"])), } ) def dot_graph(graph): return string_coin( ( "digraph {{name}}\n" + "{\n" + "{{settings_graph}}\n" + "{{settings_node}}\n" + "{{settings_edge}}\n" + "{{subgraphs}}\n" + "{{nodes}}\n" + "{{edges}}\n" + "}\n" ), { "name": graph["name"], "settings_graph": dot_entry("graph", graph["settings"]["graph"], {"indentation": 1}), "settings_node": dot_entry("node", graph["settings"]["node"], {"indentation": 1}), "settings_edge": dot_entry("edge", graph["settings"]["edge"], {"indentation": 1}), "nodes": "".join(map(lambda node: dot_node(node, {"indentation": 1}), graph["nodes"])), "edges": "".join(map(lambda edge: dot_edge(edge, {"indentation": 1}), graph["edges"])), "subgraphs": "".join(map(lambda subgraph: dot_subgraph(subgraph, {"indentation": 1}), graph["subgraphs"])), } )