Source code for pymake.plot.dependency_graphs

"""Dependency graphs for applications can be created using:

.. code-block:: python

    import os
    import pymake

    srcpth = os.path.join("..", "src")
    deppth = "dependencies"
    if not os.path.exists(deppth):
        os.makedirs(deppth)

    pymake.visualize.make_plots(srcpth, deppth, include_subdir=True)



"""

import os

import pydotplus.graphviz as pydot

from ..utils._compiler_language_files import (
    _get_ordered_srcfiles,
    _get_srcfiles,
)
from ..utils._dag import _get_f_nodelist


[docs] def to_pydot(dag, filename="mygraph.png"): """Create a png file of a Directed Acyclic Graph Parameters ---------- dag : object directed acyclic graph filename : str path of the graph png Returns ------- """ # Create the graph graph = pydot.Dot(graph_type="digraph") # Add the nodes node_dict = {} for n in dag.nodelist: pydotnode = pydot.Node(n.name, style="filled", fillcolor="red") node_dict[n] = pydotnode graph.add_node(pydotnode) # Add the edges for n in dag.nodelist: for m in n.dependencies: graph.add_edge(pydot.Edge(node_dict[n], node_dict[m])) graph.write_png(filename) return
def _add_pydot_nodes(graph, node_dict, n, ilev, level): """ Parameters ---------- graph node_dict n ilev level Returns ------- """ if ilev == level: return if n in node_dict: return ttl = os.path.basename(n.name) pydotnode = pydot.Node(ttl, style="filled", fillcolor="red", label=ttl) node_dict[n] = pydotnode graph.add_node(pydotnode) if len(n.dependencies) > 0: for m in n.dependencies: _add_pydot_nodes(graph, node_dict, m, ilev + 1, level) return def _add_pydot_edges(graph, node_dict, edge_set, n, ilev, level): """ Parameters ---------- graph node_dict edge_set n ilev level Returns ------- """ if ilev == level: return if len(n.dependencies) > 0: for m in n.dependencies: if m not in node_dict: continue tpl = (n.name, m.name) if tpl not in edge_set: edge_set.add(tpl) edge = pydot.Edge(node_dict[n], node_dict[m]) graph.add_edge(edge) _add_pydot_edges( graph, node_dict, edge_set, m, ilev + 1, level ) return
[docs] def make_plots( srcdir, outdir, include_subdir=False, level=3, extension=".png", verbose=False, networkx=False, ): """Create plots of module dependencies. Parameters ---------- srcdir : str path for source files outdir : str path for output images include_subdir : bool boolean indicating is subdirectories in the source file directory should be included level : int dependency level (1 is the minimum) extension : str output extension (default is .png) verbose : bool boolean indicating if output will be printed to the terminal networkx : bool boolean indicating that the NetworkX python package will be used to create the Directed Acyclic Graph (DAG) used to determine the order source files are compiled in. The NetworkX package tends to result in a unique DAG more often than the standard algorithm used in pymake. (default is False) Returns ------- """ srcfiles = _get_ordered_srcfiles( _get_srcfiles(srcdir, include_subdir), networkx=networkx ) nodelist = _get_f_nodelist(srcfiles) for idx, n in enumerate(nodelist): if verbose: print(f"{idx + 1:<3d}: {os.path.basename(n.name)}") for jdx, m in enumerate(n.dependencies): msg = f" {jdx + 1:<3d}: {os.path.basename(m.name)}" print(msg) if not os.path.isdir(outdir): raise Exception("output directory does not exist") for n in nodelist: filename = os.path.join(outdir, os.path.basename(n.name) + extension) if verbose: print("Creating " + filename) graph = pydot.Dot(graph_type="digraph") node_dict = {} ilev = 0 _add_pydot_nodes(graph, node_dict, n, ilev, level) edge_set = set() ilev = 1 _add_pydot_edges(graph, node_dict, edge_set, n, ilev, level) if extension == ".png": graph.write_png(filename) elif extension == ".pdf": graph.write_pdf(filename) elif extension == ".dot": graph.write_dot(filename) else: raise Exception(f"unknown file extension: {extension}") return
if __name__ == "__main__": srcdir = "src" outdir = "img" make_plots(srcdir, outdir)