""" Graphviz IPython magic extensions Magic methods: %dot %%dot %dotstr "" %dotobj obj.to_dot() %dotobjs obj[0].to_dot(), obj[1].to_dot(), ... also: %twopi, %neato, %sdp, %fsdp, and %circo magic families. Usage: %load_ext gvmagic """ from logging import info, error from subprocess import Popen, PIPE from IPython.core.display import display_svg from IPython.core.magic import Magics from IPython.core.magic import line_cell_magic from IPython.core.magic import line_magic from IPython.core.magic import magics_class def run_graphviz(s, layout_engine='dot'): """Execute dot with a layout and return a raw SVG image, or None.""" cmd = ['dot', '-Tsvg', '-K', layout_engine] dot = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) stdoutdata, stderrdata = dot.communicate(s.encode('utf-8')) status = dot.wait() if status == 0: return stdoutdata else: fstr = "dot returned {}\n[==== stderr ====]\n{}" error(fstr.format(status, stderrdata.decode('utf-8'))) return None @magics_class class GraphvizMagics(Magics): @line_cell_magic def dot(self, line, cell=None): self._from_cell(line, cell, 'dot') @line_magic def dotstr(self, line): self._from_str(line, 'dot') @line_magic def dotobj(self, line): self._from_obj(line, 'dot') @line_magic def dotobjs(self, line): self._from_objs(line, 'dot') @line_cell_magic def neato(self, line, cell=None): self._from_cell(line, cell, 'neato') @line_magic def neatostr(self, line): self._from_str(line, 'neato') @line_magic def neatoobj(self, line): self._from_obj(line, 'neato') @line_magic def neatoobjs(self, line): self._from_objs(line, 'neato') @line_cell_magic def sfdp(self, line, cell=None): self._from_cell(line, cell, 'sfdp') @line_magic def sfdpstr(self, line): self._from_str(line, 'sfdp') @line_magic def sfdpobj(self, line): self._from_obj(line, 'sfdp') @line_magic def sfdpobjs(self, line): self._from_objs(line, 'sfdp') @line_cell_magic def fdp(self, line, cell=None): self._from_cell(line, cell, 'fdp') @line_magic def fdpstr(self, line): self._from_str(line, 'fdp') @line_magic def fdpobj(self, line): self._from_obj(line, 'fdp') @line_magic def fdpobjs(self, line): self._from_objs(line, 'fdp') @line_cell_magic def twopi(self, line, cell=None): self._from_cell(line, cell, 'twopi') @line_magic def twopistr(self, line): self._from_str(line, 'twopi') @line_magic def twopiobj(self, line): self._from_obj(line, 'twopi') @line_magic def twopiobjs(self, line): self._from_objs(line, 'twopi') @line_cell_magic def circo(self, line, cell=None): self._from_cell(line, cell, 'circo') @line_magic def circostr(self, line): self._from_str(line, 'circo') @line_magic def circoobj(self, line): self._from_obj(line, 'circo') @line_magic def circoobjs(self, line): self._from_objs(line, 'circo') def _from_cell(self, line, cell, layout_engine): if cell is None: s = line else: s = line + '\n' + cell data = run_graphviz(s, layout_engine) if data: display_svg(data, raw=True) def _from_str(self, line, layout_engine): s = self.shell.ev(line) data = run_graphviz(s, layout_engine) if data: display_svg(data, raw=True) def _from_obj(self, line, layout_engine): obj = self.shell.ev(line) try: s = obj.to_dot() except AttributeError: error("expected object to implement 'to_dot()' method") except TypeError: error("expected to_dot method to be callable w/o args") else: data = run_graphviz(s, layout_engine) if data: display_svg(data, raw=True) def _from_objs(self, line, layout_engine): """dot objects magic""" objs = self.shell.ev(line) for i, obj in enumerate(objs): try: s = obj.to_dot() except AttributeError: error("expected object to implement 'to_dot()' method") except TypeError: error("expected to_dot method to be callable w/o args") else: data = run_graphviz(s, layout_engine) if data: info("object {}:".format(i)) display_svg(data, raw=True) def load_ipython_extension(ipython): """Load the extension in IPython.""" ipython.register_magics(GraphvizMagics) def unload_ipython_extension(ipython): """Unload the extension in IPython.""" pass