Commits

Takeshi Komiya committed bb60372

* Add sphinxcontrib.googlechart

  • Participants
  • Parent commits f101663

Comments (0)

Files changed (16)

 
 requirements:
    Andrey Mikhaylenko <neithere@gmail.com>
+
+googlechart:
+   Takeshi KOMIYA <i.tkomiya@gmail.com>
 - nwdiag: embed network diagrams by using nwdiag_
 - requirements: declare requirements wherever you need (e.g. in test
   docstrings), mark statuses and collect them in a single list 
+- googlechart: embed charts by using Google Chart_
 
 .. _aafigure: http://docutils.sourceforge.net/sandbox/aafigure/
 
 
 .. _nwdiag: http://blockdiag.com/
 
+.. _Google Chart: http://code.google.com/intl/ja/apis/chart/
+
 
 For contributors
 ================

File googlechart/AUTHORS

+Takeshi KOMIYA <i.tkomiya@gmail.com>

File googlechart/LICENSE

+If not otherwise noted, the extensions in this package are licensed
+under the following license.
+
+Copyright (c) 2009 by the contributors (see AUTHORS file).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File googlechart/MANIFEST.in

+include README
+include LICENSE
+include CHANGES.*

File googlechart/README

+googlechart extension README
+============================
+
+This is a sphinx extension which render charts and graphs by using
+`Google Chart <http://code.google.com/intl/ja/apis/chart/>`_ .
+
+source:
+
+.. code-block:: text
+
+   .. piechart::
+
+      dog: 100
+      cat: 80
+      rabbit: 40
+
+rendered:
+
+.. piechart::
+
+   dog: 100
+   cat: 80
+   rabbit: 40
+
+Setting
+=======
+
+You can see available package at `PyPI <http://pypi.python.org/pypi/sphinxcontrib-googlechart>`_.
+
+You can get source code at http://bitbucket.org/birkenfeld/sphinx-contrib/
+
+Install
+-------
+
+.. code-block:: bash
+
+   $ easy_install sphinxcontrib-googlechart
+
+
+Configure Sphinx
+----------------
+
+To enable this extension, add ``sphinxcontrib.googlechart`` module to extensions 
+option at :file:`conf.py`. 
+
+.. code-block:: python
+
+   # Enabled extensions
+   extensions = ['sphinxcontrib.googlechart']
+
+
+Directive
+=========
+
+.. describe:: .. piechart::
+
+   This directive insert a piechart into the generated document.
+   piechart directive takes code block as source script.
+
+   Examples::
+
+      .. piechart::
+
+         dog: 100
+         cat: 80
+         rabbit: 40
+
+   .. piechart::
+
+      dog: 100
+      cat: 80
+      rabbit: 40
+
+   If you want to change colors of chart,
+   write color parameters at source script::
+
+      .. piechart::
+
+         dog: 100
+         dog.color: ff0000
+         cat: 80
+         cat.color: 00ff00
+         rabbit: 40
+         rabbit.color: 0000ff
+
+   .. piechart::
+
+      dog: 100
+      dog.color: ff0000
+      cat: 80
+      cat.color: 00ff00
+      rabbit: 40
+      rabbit.color: 0000ff
+
+
+.. describe:: .. piechart3d::
+
+   This directive insert a 3D piechart into the generated document.
+   piechart directive takes code block as source script.
+
+   Examples::
+
+      .. piechart3d::
+         :size: 480x240
+
+         dog: 100
+         cat: 80
+         rabbit: 40
+
+   .. piechart3d::
+      :size: 480x240
+
+      dog: 100
+      cat: 80
+      rabbit: 40
+
+
+.. describe:: .. linechart::
+
+   This directive insert a linechart into the generated document.
+   linechart directive takes code block as source script.
+
+   Examples::
+
+      .. linechart::
+
+         bicycle: 15, 35, 20, 40
+         car: 60, 75, 60, 30
+
+   .. linechart::
+
+      bicycle: 15, 35, 20, 40
+      car: 60, 75, 60, 30
+
+   If you want to change colors of chart,
+   write color parameters at source script::
+
+      .. linechart::
+
+         bicycle: 15, 35, 20, 40
+         bicycle.color: ff0000
+         car: 60, 75, 60, 30
+         car.color: 0000ff
+
+   .. linechart::
+
+      bicycle: 15, 35, 20, 40
+      bicycle.color: ff0000
+      car: 60, 75, 60, 30
+      car.color: 0000ff
+
+
+.. describe:: .. linechartxy::
+
+   This directive insert a linechart into the generated document.
+   linechart directive takes code block as source script.
+
+   Examples::
+
+      .. linechartxy::
+
+         bicycle: (0, 15), (30, 35), (60, 20), (90, 40)
+         car: (0, 60), (20, 75), (40, 60), (90, 30)
+
+
+   .. linechartxy::
+
+      bicycle: (0, 15), (30, 35), (60, 20), (90, 40)
+      car: (0, 60), (20, 75), (40, 60), (90, 30)
+
+
+.. describe:: .. holizontal_barchart::
+
+   This directive insert a barchart into the generated document.
+   barchart directive takes code block as source script.
+
+   Examples::
+
+      .. holizontal_barchart::
+
+         bicycle: 15, 25, 20, 30
+         bicycle.color: ff0000
+         car: 40, 50, 60, 45
+         car.color: 0000ff
+
+   .. holizontal_barchart::
+
+      bicycle: 15, 25, 20, 30
+      bicycle.color: ff0000
+      car: 40, 50, 60, 45
+      car.color: 0000ff
+
+
+.. describe:: .. verticalbarchart::
+
+   This directive insert a barchart into the generated document.
+   barchart directive takes code block as source script.
+
+   Examples::
+
+      .. vertical_barchart::
+
+         bicycle: 15, 25, 20, 30
+         bicycle.color: ff0000
+         car: 40, 50, 60, 45
+         car.color: 0000ff
+
+   .. vertical_barchart::
+
+      bicycle: 15, 25, 20, 30
+      bicycle.color: ff0000
+      car: 40, 50, 60, 45
+      car.color: 0000ff
+
+
+.. describe:: .. holizontal_bargraph::
+
+   This directive insert a barchart into the generated document.
+   barchart directive takes code block as source script.
+
+   Examples::
+
+      .. holizontal_bargraph::
+
+         bicycle: 15, 25, 20, 30
+         bicycle.color: ff0000
+         car: 40, 50, 60, 45
+         car.color: 0000ff
+
+   .. holizontal_bargraph::
+
+      bicycle: 15, 25, 20, 30
+      bicycle.color: ff0000
+      car: 40, 50, 60, 45
+      car.color: 0000ff
+
+
+.. describe:: .. verticalbargraph::
+
+   This directive insert a barchart into the generated document.
+   barchart directive takes code block as source script.
+
+   Examples::
+
+      .. vertical_bargraph::
+
+         bicycle: 15, 25, 20, 30
+         bicycle.color: ff0000
+         car: 40, 50, 60, 45
+         car.color: 0000ff
+
+   .. vertical_bargraph::
+
+      bicycle: 15, 25, 20, 30
+      bicycle.color: ff0000
+      car: 40, 50, 60, 45
+      car.color: 0000ff
+
+
+.. describe:: .. venndiagram::
+
+   This directive insert a venn diagrams into the generated document.
+   venndiagram directive takes code block as source script.
+
+   Examples::
+
+      .. venndiagram::
+
+         data: 100, 80, 40, 20, 20, 20, 10
+
+   .. venndiagram::
+
+      data: 100, 80, 40, 20, 20, 20, 10
+
+
+.. describe:: .. plotchart::
+
+   This directive insert a plotchart into the generated document.
+   plotchart directive takes code block as source script.
+
+   Examples::
+
+      .. plotchart::
+
+         data: (50, 60), (75, 20), (20, 30), (10, 70), (45, 10)
+
+   .. plotchart::
+
+      data: (50, 60), (75, 20), (20, 30), (10, 70), (45, 10)
+
+
+Graphviz charts on Google Chart
+===============================
+
+You can use Google Chart for graphviz chart generation.
+``sphinxcontrib.googlechart.graphviz`` module is compatible with ``sphinx.ext.graphviz`` .
+This module does not depend to `graphviz` binaries.
+
+If you want to use graphviz for Google Chart` ,
+add ``sphinxcontrib.googlechart.graphviz`` module to extention option.
+
+.. code-block:: python
+
+   # Enabled extensions
+   extensions = ['sphinxcontrib.googlechart', 'sphinxcontrib.googlechart.graphviz']
+
+Examples::
+
+   .. graphviz::
+
+      digraph {
+         A -> B;
+      }
+
+.. graphviz::
+
+   digraph {
+      A -> B;
+   }
+
+
+Repository
+==========
+
+This code is hosted by Bitbucket.
+
+  http://bitbucket.org/birkenfeld/sphinx-contrib/

File googlechart/docs/conf.py

+# -*- coding: utf-8 -*-
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+html_use_modindex = False
+html_use_index = False
+extensions = ['sphinxcontrib.googlechart', 'sphinxcontrib.googlechart.graphviz']
+source_suffix = '.rst'
+master_doc = 'index'
+
+project = u'sphinxcontrib.googlchart quick reference'
+copyright = u'2011, Takeshi KOMIYA'

File googlechart/docs/index.rst

+.. include:: ../README

File googlechart/setup.cfg

+[egg_info]
+tag_build = dev
+tag_date = true
+
+[aliases]
+release = egg_info -RDb ''

File googlechart/setup.py

+# -*- coding: utf-8 -*-
+
+from setuptools import setup, find_packages
+
+long_desc = '''
+This package contains the googlechart Sphinx extension.
+
+.. _Google Chart: http://code.google.com/intl/ja/apis/chart/
+
+This extension enable you to insert charts and graphs in your Sphinx document.
+Following code is sample::
+
+   .. piechart::
+
+      dog: 100
+      cat: 80
+      rabbit: 40
+
+
+This module needs internet connection.
+
+'''
+
+requires = ['Sphinx>=0.6', 'funcparserlib']
+
+setup(
+    name='sphinxcontrib-googlechart',
+    version='0.1.0',
+    url='http://bitbucket.org/birkenfeld/sphinx-contrib',
+    download_url='http://pypi.python.org/pypi/sphinxcontrib-googlechart',
+    license='BSD',
+    author='Takeshi KOMIYA',
+    author_email='i.tkomiya@gmail.com',
+    description='Sphinx "googlechart" extension',
+    long_description=long_desc,
+    zip_safe=False,
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Environment :: Console',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Documentation',
+        'Topic :: Utilities',
+    ],
+    platforms='any',
+    packages=find_packages(),
+    include_package_data=True,
+    install_requires=requires,
+    namespace_packages=['sphinxcontrib'],
+)

File googlechart/sphinxcontrib/__init__.py

+# -*- coding: utf-8 -*-
+"""
+    sphinxcontrib
+    ~~~~~~~~~~~~~
+
+    This package is a namespace package that contains all extensions
+    distributed in the ``sphinx-contrib`` distribution.
+
+    :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+__import__('pkg_resources').declare_namespace(__name__)
+

File googlechart/sphinxcontrib/googlechart/__init__.py

+# -*- coding: utf-8 -*-
+"""
+    sphinxcontrib.googlechart
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Allow DOT like formatted charts to be included in Sphinx-generated
+    documents inline.
+
+    :copyright: Copyright 2010 by Takeshi Komiya.
+    :license: BSDL.
+"""
+
+import posixpath
+import os
+import codecs
+try:
+    from hashlib import sha1 as sha
+except ImportError:
+    from sha import sha
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+
+from sphinx.errors import SphinxError
+from sphinx.util.osutil import ensuredir, ENOENT, EPIPE
+from sphinx.util.compat import Directive
+
+from sphinxcontrib.googlechart import core
+
+
+__import__('pkg_resources').declare_namespace(__name__)
+
+
+class GoogleChartError(SphinxError):
+    category = 'GoogleChart error'
+
+
+class google_chart(nodes.General, nodes.Element):
+    pass
+
+
+class GoogleChartBase(Directive):
+    """
+    Directive to insert arbitrary dot markup.
+    """
+    has_content = True
+    required_arguments = 0
+    optional_arguments = 1
+    final_argument_whitespace = False
+    option_spec = {
+        'size': directives.unchanged,
+    }
+
+    codeheadings = None
+
+    def run(self):
+        dotcode = '\n'.join(self.content)
+        if not dotcode.strip():
+            return [self.state_machine.reporter.warning(
+                'Ignoring "chart" directive without content.',
+                line=self.lineno)]
+
+        if self.codeheadings:
+            if self.arguments:
+                graph_name = self.arguments[0]
+                dotcode = "%s %s { %s }" % (self.codeheadings, graph_name, dotcode)
+            else:
+                dotcode = "%s { %s }" % (self.codeheadings, dotcode)
+
+        node = google_chart()
+        node['code'] = dotcode
+        node['options'] = {}
+        if 'size' in self.options:
+            node['options']['size'] = self.options['size']
+
+        return [node]
+
+
+class GoogleChart(GoogleChartBase):
+    pass
+
+
+class PieChart(GoogleChartBase):
+    codeheadings = 'piechart'
+
+
+class PieChart3D(GoogleChartBase):
+    codeheadings = 'piechart_3d'
+
+
+class LineChart(GoogleChartBase):
+    codeheadings = 'linechart'
+
+
+class LineChartXY(GoogleChartBase):
+    codeheadings = 'linechartxy'
+
+
+class HolizontalBarChart(GoogleChartBase):
+    codeheadings = 'holizontal_barchart'
+
+
+class VerticalBarChart(GoogleChartBase):
+    codeheadings = 'vertical_barchart'
+
+
+class HolizontalBarGraph(GoogleChartBase):
+    codeheadings = 'holizontal_bargraph'
+
+
+class VerticalBarGraph(GoogleChartBase):
+    codeheadings = 'vertical_bargraph'
+
+
+class VennDiagram(GoogleChartBase):
+    codeheadings = 'venndiagram'
+
+
+class PlotChart(GoogleChartBase):
+    codeheadings = 'plotchart'
+
+
+# compatibility to sphinx 1.0 (ported from sphinx trunk)
+def relfn2path(env, filename, docname=None):
+    if filename.startswith('/') or filename.startswith(os.sep):
+        rel_fn = filename[1:]
+    else:
+        docdir = os.path.dirname(env.doc2path(docname or env.docname,
+                                              base=None))
+        rel_fn = os.path.join(docdir, filename)
+    try:
+        return rel_fn, os.path.join(env.srcdir, rel_fn)
+    except UnicodeDecodeError:
+        # the source directory is a bytestring with non-ASCII characters;
+        # let's try to encode the rel_fn in the file system encoding
+        enc_rel_fn = rel_fn.encode(sys.getfilesystemencoding())
+        return rel_fn, os.path.join(env.srcdir, enc_rel_fn)
+
+
+def get_image_filename(self, code, options, prefix='google_chart'):
+    """
+    Get path of output file.
+    """
+    hashkey = code.encode('utf-8') + str(options)
+    fname = '%s-%s.png' % (prefix, sha(hashkey).hexdigest())
+    if hasattr(self.builder, 'imgpath'):
+        # HTML
+        relfn = posixpath.join(self.builder.imgpath, fname)
+        outfn = os.path.join(self.builder.outdir, '_images', fname)
+    else:
+        # LaTeX
+        relfn = fname
+        outfn = os.path.join(self.builder.outdir, fname)
+
+    if os.path.isfile(outfn):
+        return relfn, outfn
+
+    ensuredir(os.path.dirname(outfn))
+
+    return relfn, outfn
+
+
+def create_google_chart(self, code, format, filename, options, prefix='google_chart'):
+    """
+    Render google_chart code into a image file.
+    """
+    kwargs = {}
+    if options.has_key('size'):
+        kwargs['size'] = options['size']
+
+    try:
+        chart = core.GoogleChart(code, 'chart', **kwargs)
+        chart.save(filename)
+    except Exception, e:
+        raise GoogleChartError(e)
+
+
+def render_dot_html(self, node, code, options, prefix='google_chart',
+                    imgcls=None, alt=None):
+    has_thumbnail = False
+    try:
+        relfn, outfn = get_image_filename(self, code, options, prefix)
+        if not os.path.isfile(outfn):
+            create_google_chart(self, code, format, outfn, options, prefix)
+    except GoogleChartError, exc:
+        self.builder.warn('dot code %r: ' % code + str(exc))
+        raise nodes.SkipNode
+
+    self.body.append(self.starttag(node, 'p', CLASS='google_chart'))
+    if relfn is None:
+        self.body.append(self.encode(code))
+    else:
+        if alt is None:
+            alt = node.get('alt', self.encode(code).strip())
+
+        imgtag_format = '<img src="%s" alt="%s" />\n'
+        self.body.append(imgtag_format % (relfn, alt))
+
+    self.body.append('</p>\n')
+    raise nodes.SkipNode
+
+
+def html_visit_google_chart(self, node):
+    render_dot_html(self, node, node['code'], node['options'])
+
+
+def render_dot_latex(self, node, code, options, prefix='google_chart'):
+    try:
+        fname, outfn = get_image_filename(self, code, options, prefix)
+        create_google_chart(self, code, format, outfn, options, prefix)
+    except GoogleChartError, exc:
+        self.builder.warn('dot code %r: ' % code + str(exc))
+        raise nodes.SkipNode
+
+    if fname is not None:
+        self.body.append('\\par\\includegraphics{%s}\\par' % fname)
+    raise nodes.SkipNode
+
+
+def latex_visit_google_chart(self, node):
+    render_dot_latex(self, node, node['code'], node['options'])
+
+
+def setup(app):
+    app.add_node(google_chart,
+                 html=(html_visit_google_chart, None),
+                 latex=(latex_visit_google_chart, None))
+    app.add_directive('chart', GoogleChart)
+    app.add_directive('piechart', PieChart)
+    app.add_directive('piechart3d', PieChart3D)
+    app.add_directive('linechart', LineChart)
+    app.add_directive('linechartxy', LineChartXY)
+    app.add_directive('holizontal_barchart', HolizontalBarChart)
+    app.add_directive('vertical_barchart', VerticalBarChart)
+    app.add_directive('holizontal_bargraph', HolizontalBarGraph)
+    app.add_directive('vertical_bargraph', VerticalBarGraph)
+    app.add_directive('venndiagram', VennDiagram)
+    app.add_directive('plotchart', PlotChart)

File googlechart/sphinxcontrib/googlechart/core.py

+# -*- coding: utf-8 -*-
+
+import re
+import urllib
+from parser import parse_string
+
+
+class GoogleChartError(Exception):
+    pass
+
+
+class GoogleChart(object):
+    baseurl = 'https://chart.googleapis.com/chart?'
+
+    def __init__(self, code, format, **options):
+        self.code = code
+        self.format = format
+        self.options = options
+
+    @property
+    def url(self):
+        if self.format == 'chart':
+            params = self._url_for_chart()
+        elif self.format == 'graphviz':
+            params = self._url_for_graphviz()
+        else:
+            msg = "unknown format: %s" % self.format
+            raise GoogleChartError(msg)
+
+        quoted = ("%s=%s" % (k, urllib.quote(v)) for k, v in params.items())
+        url = self.baseurl + "&".join(quoted)
+
+        return url
+
+    def _url_for_chart(self):
+        chart = parse_string(self.code)
+        if chart.type in ('p', 'p3'):
+            params = self._url_for_piechart(chart)
+        elif chart.type in ('lc',):
+            params = self._url_for_linechart(chart)
+        elif chart.type in ('lxy',):
+            params = self._url_for_linechart_xy(chart)
+        elif chart.type in ('bhs', 'bvs', 'bhg', 'bvg'):
+            params = self._url_for_barchart(chart)
+        elif chart.type in ('v'):
+            params = self._url_for_venndiagram(chart)
+        elif chart.type in ('s'):
+            params = self._url_for_spotchart(chart)
+        else:
+            params = {}
+
+        params['cht'] = chart.type
+        params['chs'] = self.options.get('size', '320x240')
+
+        return params
+
+    def _url_for_piechart(self, chart):
+        data = "t:" + ",".join(zip(*chart.items)[0])
+        colors = ",".join(chart.colors)
+        labels = "|".join(chart.labels)
+
+        return dict(chd=data, chl=labels, chco=colors)
+
+    def _url_for_linechart(self, chart):
+        series = (",".join(n) for n in chart.items)
+        data = "t:" + "|".join(series)
+        colors = ",".join(chart.colors)
+        labels = "|".join(chart.labels)
+
+        return dict(chd=data, chdl=labels, chco=colors)
+
+    def _url_for_linechart_xy(self, chart):
+        series = []
+        [series.extend(m) for m in (zip(*n) for n in chart.items)]
+        data = "t:" + "|".join(",".join(n) for n in series)
+        colors = ",".join(chart.colors)
+        labels = "|".join(chart.labels)
+
+        return dict(chd=data, chdl=labels, chco=colors)
+
+    def _url_for_barchart(self, chart):
+        data = "t:" + "|".join(",".join(n) for n in chart.items)
+        colors = ",".join(chart.colors)
+        labels = "|".join(chart.labels)
+
+        return dict(chd=data, chdl=labels, chco=colors)
+
+    def _url_for_venndiagram(self, chart):
+        data = "t:" + ",".join(chart.items.next())
+
+        return dict(chd=data)
+
+    def _url_for_venndiagram(self, chart):
+        data = "t:" + ",".join(chart.items.next())
+
+        return dict(chd=data)
+
+    def _url_for_spotchart(self, chart):
+        data = "t:" + "|".join(",".join(n) for n in zip(*chart.items.next()))
+
+        return dict(chd=data)
+
+    def _url_for_graphviz(self):
+        _type = self.options.get('type', 'dot')
+        striped = re.sub('\\s+', ' ', self.code)
+
+        params = dict(cht="gv:%s" % _type, chl=striped)
+
+        if self.options.get('size'):
+            params['chs'] = self.options.get('size')
+
+        return params
+
+    def save(self, filename):
+        fd = urllib.urlopen(self.url)
+
+        if fd.getcode() == 200:
+            open(filename, 'w').write(fd.read())
+        else:
+            msg = "google chart error: a malformed or illegal request"
+            raise GoogleChartError(msg)

File googlechart/sphinxcontrib/googlechart/graphviz.py

+# -*- coding: utf-8 -*-
+"""
+    sphinxcontrib.googlechart.graphviz
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Allow graphviz-formatted diagrams to be included in Sphinx-generated
+    documents inline.
+
+    :copyright: Copyright 2010 by Takeshi Komiya.
+    :license: BSDL.
+"""
+
+import posixpath
+import os
+import codecs
+try:
+    from hashlib import sha1 as sha
+except ImportError:
+    from sha import sha
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+
+from sphinx.errors import SphinxError
+from sphinx.util.osutil import ensuredir, ENOENT, EPIPE
+from sphinx.util.compat import Directive
+
+from sphinxcontrib.googlechart.core import GoogleChart
+
+
+class GraphvizError(SphinxError):
+    category = 'Graphviz error'
+
+
+class graphviz(nodes.General, nodes.Element):
+    pass
+
+
+class GraphvizBase(Directive):
+    """
+    Directive to insert arbitrary dot markup.
+    """
+    has_content = True
+    required_arguments = 0
+    optional_arguments = 1
+    final_argument_whitespace = False
+    option_spec = {
+        'type': directives.unchanged,
+        'size': directives.unchanged,
+    }
+
+    codeheadings = None
+
+    def run(self):
+        dotcode = '\n'.join(self.content)
+        if not dotcode.strip():
+            return [self.state_machine.reporter.warning(
+                'Ignoring "graphviz" directive without content.',
+                line=self.lineno)]
+
+        if self.codeheadings:
+            if self.arguments:
+                graph_name = self.arguments[0]
+                dotcode = "%s %s { %s }" % (self.codeheadings, graph_name, dotcode)
+            else:
+                dotcode = "%s { %s }" % (self.codeheadings, dotcode)
+
+        node = graphviz()
+        node['code'] = dotcode
+        node['options'] = {}
+        if 'type' in self.options:
+            node['options']['type'] = self.options['type']
+        if 'size' in self.options:
+            node['options']['size'] = self.options['size']
+
+        return [node]
+
+
+class Graphviz(GraphvizBase):
+    pass
+
+
+class Graphviz_Graph(GraphvizBase):
+    required_arguments = 0
+    codeheadings = 'graph'
+
+
+class Graphviz_Digraph(GraphvizBase):
+    required_arguments = 0
+    codeheadings = 'digraph'
+
+
+# compatibility to sphinx 1.0 (ported from sphinx trunk)
+def relfn2path(env, filename, docname=None):
+    if filename.startswith('/') or filename.startswith(os.sep):
+        rel_fn = filename[1:]
+    else:
+        docdir = os.path.dirname(env.doc2path(docname or env.docname,
+                                              base=None))
+        rel_fn = os.path.join(docdir, filename)
+    try:
+        return rel_fn, os.path.join(env.srcdir, rel_fn)
+    except UnicodeDecodeError:
+        # the source directory is a bytestring with non-ASCII characters;
+        # let's try to encode the rel_fn in the file system encoding
+        enc_rel_fn = rel_fn.encode(sys.getfilesystemencoding())
+        return rel_fn, os.path.join(env.srcdir, enc_rel_fn)
+
+
+def get_image_filename(self, code, options, prefix='graphviz'):
+    """
+    Get path of output file.
+    """
+    hashkey = code.encode('utf-8') + str(options)
+    fname = '%s-%s.png' % (prefix, sha(hashkey).hexdigest())
+    if hasattr(self.builder, 'imgpath'):
+        # HTML
+        relfn = posixpath.join(self.builder.imgpath, fname)
+        outfn = os.path.join(self.builder.outdir, '_images', fname)
+    else:
+        # LaTeX
+        relfn = fname
+        outfn = os.path.join(self.builder.outdir, fname)
+
+    if os.path.isfile(outfn):
+        return relfn, outfn
+
+    ensuredir(os.path.dirname(outfn))
+
+    return relfn, outfn
+
+
+def create_graphviz(self, code, format, filename, options, prefix='graphviz'):
+    """
+    Render graphviz code into a image file.
+    """
+    try:
+        chart = GoogleChart(code, 'graphviz', type=options.get('type', 'dot'), size=options.get('size'))
+        chart.save(filename)
+    except:
+        raise GraphvizError('graphviz error: a malformed or illegal request:')
+
+
+def render_dot_html(self, node, code, options, prefix='graphviz',
+                    imgcls=None, alt=None):
+    has_thumbnail = False
+    try:
+        relfn, outfn = get_image_filename(self, code, options, prefix)
+        if not os.path.isfile(outfn):
+            create_graphviz(self, code, format, outfn, options, 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 relfn is None:
+        self.body.append(self.encode(code))
+    else:
+        if alt is None:
+            alt = node.get('alt', self.encode(code).strip())
+
+        imgtag_format = '<img src="%s" alt="%s" />\n'
+        self.body.append(imgtag_format % (relfn, alt))
+
+    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, outfn = get_image_filename(self, code, options, prefix)
+        create_graphviz(self, code, format, outfn, options, prefix)
+    except GraphvizError, exc:
+        self.builder.warn('dot code %r: ' % code + str(exc))
+        raise nodes.SkipNode
+
+    if fname is not None:
+        self.body.append('\\par\\includegraphics{%s}\\par' % 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', Graphviz_Graph)
+    app.add_directive('digraph', Graphviz_Digraph)

File googlechart/sphinxcontrib/googlechart/parser.py

+# -*- coding: utf-8 -*-
+
+import codecs
+from re import MULTILINE, DOTALL
+from funcparserlib.lexer import make_tokenizer, Token, LexerError
+from funcparserlib.parser import (some, a, maybe, many, finished, skip,
+    oneplus, forward_decl, NoParseError)
+
+from collections import namedtuple
+
+ENCODING = 'utf-8'
+
+Chart = namedtuple('Chart', 'type id stmts')
+Node = namedtuple('Node', 'id attrs')
+Attr = namedtuple('Attr', 'name value')
+DefAttrs = namedtuple('DefAttrs', 'object attrs')
+
+
+class ParseException(Exception):
+    pass
+
+
+def tokenize(str):
+    'str -> Sequence(Token)'
+    specs = [
+        ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)),
+        ('Comment', (r'//.*',)),
+        ('NL',      (r'[\r\n]+',)),
+        ('Space',   (r'[ \t\r\n]+',)),
+        ('Name',    (ur'[A-Za-z_\u0080-\uffff][A-Za-z_0-9\.\u0080-\uffff]*',)),
+        ('Op',      (r'[{}():;,=\[\]]',)),
+        ('Color',  (r'[A-Za-z0-9]+',)),
+        ('Number',  (r'-?(\.[0-9]+)|([0-9]+(\.[0-9]*)?)',)),
+        ('String',  (r'(?P<quote>"|\').*?(?<!\\)(?P=quote)', DOTALL)),
+    ]
+    useless = ['Comment', 'NL', 'Space']
+    t = make_tokenizer(specs)
+    return [x for x in t(str) if x.type not in useless]
+
+
+def parse(seq):
+    'Sequence(Token) -> object'
+    unarg = lambda f: lambda args: f(*args)
+    tokval = lambda x: x.value
+    flatten = lambda list: sum(list, [])
+    value_flatten = lambda l: sum([[l[0]]] + list(l[1:]), [])
+    n = lambda s: a(Token('Name', s)) >> tokval
+    op = lambda s: a(Token('Op', s)) >> tokval
+    op_ = lambda s: skip(op(s))
+    id = some(lambda t:
+        t.type in ['Name', 'Number', 'Color', 'String']).named('id') >> tokval
+    make_chart_attr = lambda args: DefAttrs(u'chart', [Attr(*args)])
+
+    node_id = id  # + maybe(port)
+    pair = (
+        op_('(') + id + skip(maybe(op(','))) + id + op_(')')
+        >> tuple)
+    value = (id | pair)
+    value_list = (
+        value +
+        many(op_(',') + value)
+        >> value_flatten)
+    a_list = (
+        id +
+        maybe(op_('=') + id) +
+        skip(maybe(op(',')))
+        >> unarg(Attr))
+    attr_list = (
+        many(op_('[') + many(a_list) + op_(']'))
+        >> flatten)
+    chart_attr = id + (op_('=') | op_(':')) + value_list >> make_chart_attr
+    node_stmt = node_id + attr_list >> unarg(Node)
+
+    stmt = (
+        chart_attr
+        | node_stmt
+    )
+    stmt_list = many(stmt + skip(maybe(op(';'))))
+    chart_type = (
+          n('p')   | n('pie')    | n('piechart')
+        | n('p3')  | n('pie3d')  | n('piechart_3d')
+        | n('lc')  | n('line')   | n('linechart')
+        | n('lxy') | n('linechartxy')
+        | n('bhs') | n('holizontal_barchart')
+        | n('bvs') | n('vertical_barchart')
+        | n('bhg') | n('holizontal_bargraph')
+        | n('bvg') | n('vertical_bargraph')
+        | n('v')   | n('venn')   | n('venndiagram')
+        | n('s')   | n('plot')   | n('plotchart')
+    )
+    chart = (
+        chart_type +
+        maybe(id) +
+        op_('{') +
+        stmt_list +
+        op_('}')
+        >> unarg(Chart))
+    dotfile = chart + skip(finished)
+
+    return dotfile.parse(seq)
+
+
+chart_types = dict(p='p',     pie='p',    piechart='p',
+                   p3='p3',   pie3d='p3', piechart_3d='p3',
+                   lc='lc',   line='lc',  linechart='lc',
+                   lxy='lxy', linechartxy='lxy',
+                   bhs='bhs', holizontal_barchart='bhs',
+                   bvs='bvs', vertical_barchart='bvs',
+                   bhg='bhg', holizontal_bargraph='bhg',
+                   bvg='bvg', vertical_bargraph='bvg',
+                   v='v',     venn='v',   venndiagram='v',
+                   s='s',     plot='s',   plotchart='s',
+              )
+
+
+class _Chart(object):
+    def __init__(self, _type):
+        self.type = chart_types[_type]
+        self._items = {}
+        self._order = []
+
+    def set(self, name, value):
+        self._items[name] = value
+
+        if "." in name:
+            n = name.index(".")
+            key = name[0:n]
+        else:
+            key = name
+
+        if key not in self._order:
+            self._order.append(key)
+
+    @property
+    def labels(self):
+        return self._order
+
+    @property
+    def items(self):
+        for key in self._order:
+            yield self._items[key]
+
+    @property
+    def colors(self):
+        colors = []
+        for key in self._order:
+            key += ".color"
+
+            if key in self._items:
+                colors.append(self._items[key][0])
+            else:
+                break
+
+        return colors
+
+
+def parse_string(code):
+    tree = parse(tokenize(code))
+
+    chart = _Chart(tree.type)
+
+    for stmt in tree.stmts:
+        if isinstance(stmt, DefAttrs):
+            for attr in stmt.attrs:
+                chart.set(attr.name, attr.value)
+
+    return chart
+
+
+def parse_file(path):
+    try:
+        input = codecs.open(path, 'r', 'utf-8').read()
+        return parse_string(input)
+    except LexerError, e:
+        message = "Got unexpected token at line %d column %d" % e.place
+        raise ParseException(message)
+    except Exception, e:
+        raise ParseException(str(e))

File googlechart/tox.ini

+## configuration for tox <http://codespeak.net/tox/>
+
+## tox automates running certain tasks within virtualenvs.  The following
+## tox configuration outlines a basic setup for running unit tests and
+## building sphinx docs in separate virtual environments.  Give it a try!
+
+[tox]
+envlist=python,doc
+
+# test running
+[testenv:python]
+deps=
+    ## if you use nose for test running
+    # nose
+    ## if you use py.test for test running
+    # pytest
+commands=
+    ## run tests with py.test
+    # py.test []
+    ## run tests with nose
+    # nose
+
+[testenv:doc]
+deps=
+    sphinx
+    # add all Sphinx extensions and other dependencies required to build your docs
+commands=
+    ## test links
+    # sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck
+    ## test html output
+    # sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html
+