diff options
| author | Georg Brandl <georg@python.org> | 2009-03-04 23:52:56 +0100 |
|---|---|---|
| committer | Georg Brandl <georg@python.org> | 2009-03-04 23:52:56 +0100 |
| commit | 2c004ae5097979e06029a7ce0df6fad6301c3874 (patch) | |
| tree | fbbc316dc5e6800c0fc2f58a2d6af1547013e7a8 | |
| parent | e444166923eb4e5cc16d15f5ccfe84adf9d01ba4 (diff) | |
| download | sphinx-2c004ae5097979e06029a7ce0df6fad6301c3874.tar.gz | |
New ``graphviz`` extension to embed graphviz graphs.
| -rw-r--r-- | AUTHORS | 1 | ||||
| -rw-r--r-- | CHANGES | 2 | ||||
| -rw-r--r-- | doc/ext/graphviz.rst | 77 | ||||
| -rw-r--r-- | doc/extensions.rst | 2 | ||||
| -rw-r--r-- | sphinx/ext/graphviz.py | 182 |
5 files changed, 264 insertions, 0 deletions
@@ -6,6 +6,7 @@ Substantial parts of the templates were written by Armin Ronacher Other contributors, listed alphabetically, are: * Daniel Bültmann -- todo extension +* Charles Duffy -- original graphviz extension * Josip Dzolonga -- coverage builder * Horst Gutmann -- internationalization support * Martin Hans -- autodoc improvements @@ -137,6 +137,8 @@ New features added * Extensions and API: + - New ``graphviz`` extension to embed graphviz graphs. + - Autodoc now has a reusable Python API, which can be used to create custom types of objects to auto-document (e.g. Zope interfaces). See also ``Sphinx.add_autodocumenter()``. diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst new file mode 100644 index 00000000..1d4ed807 --- /dev/null +++ b/doc/ext/graphviz.rst @@ -0,0 +1,77 @@ +.. highlight:: rest + +The Graphviz extension +====================== + +.. module:: sphinx.ext.graphviz + :synopsis: Support for Graphviz graphs. + +.. versionadded:: 0.6 + +This extension allows you to embed `Graphviz <http://graphviz.org/>`_ graphs in +your documents. + +It adds these directives: + + +.. directive:: graphviz + + Directive to embed graphviz code. The input code for ``dot`` is given as the + content. For example:: + + .. graphviz:: + + digraph foo { + "bar" -> "baz"; + } + + In HTML output, the code will be rendered to a PNG image. In LaTeX output, + the code will be rendered to an embeddable PDF file. + + +.. directive:: graph + + Directive for embedding a single undirected graph. The name is given as a + directive argument, the contents of the graph are the directive content. + This is a convenience directive to generate ``graph <name> { <content> }``. + + For example:: + + .. graph:: foo + + "bar" -- "baz"; + + +.. directive:: digraph + + Directive for embedding a single directed graph. The name is given as a + directive argument, the contents of the graph are the directive content. + This is a convenience directive to generate ``digraph <name> { <content> }``. + + For example:: + + .. digraph:: foo + + "bar" -> "baz" -> "quux"; + + +There are also these new config values: + +.. confval:: graphviz_dot + + The command name with which to invoke ``dot``. The default is ``'dot'``; you + may need to set this to a full path if ``dot`` is not in the executable + search path. + + Since this setting is not portable from system to system, it is normally not + useful to set it in ``conf.py``; rather, giving it on the + :program:`sphinx-build` command line via the :option:`-D` option should be + preferable, like this:: + + sphinx-build -b html -D graphviz_dot=C:\graphviz\bin\dot.exe . _build/html + +.. confval:: graphviz_dot_args + + Additional command-line arguments to give to dot, as a list. The default is + an empty list. This is the right place to set global graph, node or edge + attributes via dot's ``-G``, ``-N`` and ``-E`` options. diff --git a/doc/extensions.rst b/doc/extensions.rst index 12c82da5..21ba0fd8 100644 --- a/doc/extensions.rst +++ b/doc/extensions.rst @@ -44,6 +44,8 @@ These extensions are built in and can be activated by respective entries in the ext/doctest ext/intersphinx ext/math + ext/graphviz + ext/inheritance ext/refcounting ext/ifconfig ext/coverage diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py new file mode 100644 index 00000000..084797b7 --- /dev/null +++ b/sphinx/ext/graphviz.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +""" + sphinx.ext.graphviz + ~~~~~~~~~~~~~~~~~~~ + + Allow graphviz-formatted graphs to be included in Sphinx-generated + documents inline. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import sys +import posixpath +from os import path +from subprocess import Popen, PIPE +try: + from hashlib import sha1 as sha +except ImportError: + from sha import sha + +from docutils import nodes + +from sphinx.errors import SphinxError +from sphinx.util import ensuredir +from sphinx.util.compat import Directive + + +mapname_re = re.compile(r'<map id="(.*?)"') + + +class GraphvizError(SphinxError): + category = 'Graphviz error' + + +class graphviz(nodes.General, nodes.Element): + pass + + +class Graphviz(Directive): + """ + Directive to insert arbitrary dot markup. + """ + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + node = graphviz() + node['code'] = '\n'.join(self.content) + node['options'] = [] + return [node] + + +class GraphvizSimple(Directive): + """ + Directive to insert arbitrary dot markup. + """ + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + node = graphviz() + node['code'] = '%s %s {\n%s\n}\n' % \ + (self.name, self.arguments[0], '\n'.join(self.content)) + node['options'] = [] + return [node] + + +def render_dot(self, code, options, format, prefix='graphviz'): + """ + Render graphviz code into a PNG or PDF output file. + """ + hashkey = code.encode('utf-8') + str(options) + \ + str(self.builder.config.graphviz_dot_args) + fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format) + if hasattr(self.builder, 'imgpath'): + # HTML + relfn = posixpath.join(self.builder.imgpath, fname) + outfn = path.join(self.builder.outdir, '_images', fname) + else: + # LaTeX + relfn = fname + outfn = path.join(self.builder.outdir, fname) + + if path.isfile(outfn): + return relfn + + if hasattr(self.builder, '_graphviz_warned_dot') or \ + hasattr(self.builder, '_graphviz_warned_ps2pdf'): + return None + + ensuredir(path.dirname(outfn)) + + dot_args = [self.builder.config.graphviz_dot] + dot_args.extend(self.builder.config.graphviz_dot_args) + dot_args.extend(options) + dot_args.extend(['-T' + format, '-o' + outfn]) + if format == 'png': + dot_args.extend(['-Tcmapx', '-o%s.map' % outfn]) + try: + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE) + except OSError, err: + if err.errno != 2: # No such file or directory + raise + self.builder.warn('dot command %r cannot be run (needed for graphviz ' + 'output), check the graphviz_dot setting' % + self.builder.config.graphviz_dot) + self.builder._graphviz_warned_dot = True + return None + stdout, stderr = p.communicate(code) + if p.returncode != 0: + raise GraphvizError('dot exited with error:\n[stderr]\n%s\n' + '[stdout]\n%s' % (stderr, stdout)) + return relfn + + +def render_dot_html(self, node, code, options, prefix='graphviz', imgcls=None): + try: + fname = render_dot(self, code, options, 'png', prefix) + except GraphvizError, exc: + self.builder.warn('dot code %r: ' % code + str(exc)) + raise nodes.SkipNode + + self.body.append(self.starttag(node, 'p', CLASS='graphviz')) + if fname is None: + self.body.append(self.encode(code)) + else: + mapfile = open(path.join(self.builder.outdir, fname) + '.map', 'rb') + imgmap = mapfile.readlines() + mapfile.close() + imgcss = imgcls and 'class="%s"' % imgcls or '' + if len(imgmap) == 2: + # nothing in image map (the lines are <map> and </map>) + self.body.append('<img src="%s" alt="%s" %s/>\n' % + (fname, self.encode(code).strip(), imgcss)) + else: + # has a map: get the name of the map and connect the parts + mapname = mapname_re.match(imgmap[0]).group(1) + self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' % + (fname, self.encode(code).strip(), + mapname, imgcss)) + self.body.extend(imgmap) + self.body.append('</p>\n') + raise nodes.SkipNode + + +def html_visit_graphviz(self, node): + render_dot_html(self, node, node['code'], node['options']) + + +def render_dot_latex(self, node, code, options, prefix='graphviz'): + try: + fname = render_dot(self, code, options, 'pdf', prefix) + except GraphvizError, exc: + self.builder.warn('dot code %r: ' % code + str(exc)) + raise nodes.SkipNode + + if fname is not None: + self.body.append('\\includegraphics{%s}' % fname) + raise nodes.SkipNode + + +def latex_visit_graphviz(self, node): + render_dot_latex(self, node, node['code'], node['options']) + +def setup(app): + app.add_node(graphviz, + html=(html_visit_graphviz, None), + latex=(latex_visit_graphviz, None)) + app.add_directive('graphviz', Graphviz) + app.add_directive('graph', GraphvizSimple) + app.add_directive('digraph', GraphvizSimple) + app.add_config_value('graphviz_dot', 'dot', 'html') + app.add_config_value('graphviz_dot_args', [], 'html') |
