mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
New `graphviz
` extension to embed graphviz graphs.
This commit is contained in:
parent
1a22255080
commit
75637a5d6a
1
AUTHORS
1
AUTHORS
@ -6,6 +6,7 @@ Substantial parts of the templates were written by Armin Ronacher
|
|||||||
Other contributors, listed alphabetically, are:
|
Other contributors, listed alphabetically, are:
|
||||||
|
|
||||||
* Daniel Bültmann -- todo extension
|
* Daniel Bültmann -- todo extension
|
||||||
|
* Charles Duffy -- original graphviz extension
|
||||||
* Josip Dzolonga -- coverage builder
|
* Josip Dzolonga -- coverage builder
|
||||||
* Horst Gutmann -- internationalization support
|
* Horst Gutmann -- internationalization support
|
||||||
* Martin Hans -- autodoc improvements
|
* Martin Hans -- autodoc improvements
|
||||||
|
2
CHANGES
2
CHANGES
@ -137,6 +137,8 @@ New features added
|
|||||||
|
|
||||||
* Extensions and API:
|
* Extensions and API:
|
||||||
|
|
||||||
|
- New ``graphviz`` extension to embed graphviz graphs.
|
||||||
|
|
||||||
- Autodoc now has a reusable Python API, which can be used to
|
- Autodoc now has a reusable Python API, which can be used to
|
||||||
create custom types of objects to auto-document (e.g. Zope
|
create custom types of objects to auto-document (e.g. Zope
|
||||||
interfaces). See also ``Sphinx.add_autodocumenter()``.
|
interfaces). See also ``Sphinx.add_autodocumenter()``.
|
||||||
|
77
doc/ext/graphviz.rst
Normal file
77
doc/ext/graphviz.rst
Normal file
@ -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.
|
@ -44,6 +44,8 @@ These extensions are built in and can be activated by respective entries in the
|
|||||||
ext/doctest
|
ext/doctest
|
||||||
ext/intersphinx
|
ext/intersphinx
|
||||||
ext/math
|
ext/math
|
||||||
|
ext/graphviz
|
||||||
|
ext/inheritance
|
||||||
ext/refcounting
|
ext/refcounting
|
||||||
ext/ifconfig
|
ext/ifconfig
|
||||||
ext/coverage
|
ext/coverage
|
||||||
|
182
sphinx/ext/graphviz.py
Normal file
182
sphinx/ext/graphviz.py
Normal file
@ -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')
|
Loading…
Reference in New Issue
Block a user