Better Documentation Through Automation: Creating docutils & Sphinx Extensions

Preview:

DESCRIPTION

Sphinx is an incredibly useful tool for creating attractive documentation for your project, but if all you ever use it for is converting reStructuredText files to HTML you are barely scratching the surface of its power. This presentation shows how easy it is to extend Sphinx by defining new markup processors, allowing you to take your documentation to the next level. PyCon 2013

Citation preview

Better Documentation Through Automation:Creating docutils & Sphinx Extensions

Doug Hellmann@doughellmannPyCon 2013

Saturday, March 16, 13

Markup Language

Saturday, March 16, 13

Markup LanguageExtensible

Saturday, March 16, 13

Saturday, March 16, 13

SphinxApplication

Saturday, March 16, 13

BuildEnvironment

SphinxApplication

.rst

Saturday, March 16, 13

BuildEnvironment

docutilsparser

SphinxApplication

.rst

Saturday, March 16, 13

BuildEnvironment

docutilsparser

SphinxBuilder

SphinxApplication

.rst .pdf .html

Saturday, March 16, 13

section

title paragraph

#text emphasis #text literal

#text#text

#text

Saturday, March 16, 13

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vulputate elementum lectus a viverra. Vivamus hendrerit egestas lacinia. Proin tellus lectus, scelerisque vitae rutrum eget, ultrices vel metus. Quisque :ref:`target-name` pharetra lorem vehicula lorem posuere molestie cursus ipsum ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas eu sapien at tellus suscipit aliquet :pep:`8` ut non tellus. Nulla facilisis bibendum dolor, quis mattis urna posuere eget. Nulla quis dui id augue faucibus varius. Nam eu leo quam. Fusce :term:`condimentum` placerat nisi nec vestibulum. Duis tortor sapien, commodo ac faucibus nec :rfc:`1822`, rutrum quis urna. Pellentesque cursus facilisis odio, id aliquam sem commodo vestibulum. Proin eget velit sed justo ultricies vulputate.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vulputate elementum lectus a viverra. Vivamus hendrerit egestas lacinia. Proin tellus lectus, scelerisque vitae rutrum eget, ultrices vel metus. Quisque :ref:`target-name` pharetra lorem vehicula lorem posuere molestie cursus ipsum ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas eu sapien at tellus suscipit aliquet :pep:`8` ut non tellus. Nulla facilisis bibendum dolor, quis mattis urna posuere eget. Nulla quis dui id augue faucibus varius. Nam eu leo quam. Fusce :term:`condimentum` placerat nisi nec vestibulum. Duis tortor sapien, commodo ac faucibus nec :rfc:`1822`, rutrum quis urna. Pellentesque cursus facilisis odio, id aliquam sem commodo vestibulum. Proin eget velit sed justo ultricies vulputate.

Saturday, March 16, 13

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vulputate elementum lectus a viverra. Vivamus hendrerit egestas lacinia. Proin tellus lectus, scelerisque vitae rutrum eget, ultrices vel metus. Quisque :ref:`target-name` pharetra lorem vehicula lorem posuere molestie cursus ipsum ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas eu sapien at tellus suscipit aliquet :pep:`8` ut non tellus. Nulla facilisis bibendum dolor, quis mattis urna posuere eget. Nulla quis dui id augue faucibus varius. Nam eu leo quam. Fusce :term:`condimentum` placerat nisi nec vestibulum. Duis tortor sapien, commodo ac faucibus nec :rfc:`1822`, rutrum quis urna. Pellentesque cursus facilisis odio, id aliquam sem commodo vestibulum. Proin eget velit sed justo ultricies vulputate.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vulputate elementum lectus a viverra. Vivamus hendrerit egestas lacinia. Proin tellus lectus, scelerisque vitae rutrum eget, ultrices vel metus. Quisque :ref:`target-name` pharetra lorem vehicula lorem posuere molestie cursus ipsum ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas eu sapien at tellus suscipit aliquet :pep:`8` ut non tellus. Nulla facilisis bibendum dolor, quis mattis urna posuere eget. Nulla quis dui id augue faucibus varius. Nam eu leo quam. Fusce :term:`condimentum` placerat nisi nec vestibulum. Duis tortor sapien, commodo ac faucibus nec :rfc:`1822`, rutrum quis urna. Pellentesque cursus facilisis odio, id aliquam sem commodo vestibulum. Proin eget velit sed justo ultricies vulputate.

Saturday, March 16, 13

.. seealso::

PyEnchant_ Python interface to enchant_.

:ref:`project-sphinxcontrib-spelling` Project home page for the spelling checker.

sphinxcontrib_ BitBucket repository for sphinxcontrib-spelling and several other Sphinx extensions.

.. include:: example.py :literal: :start-after: # end-of-header-comment

.. image:: figure.png

Saturday, March 16, 13

2.6

- Fixed a problem with hook script line endings under Cygwin (`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-versions`) (`Issue 70 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/70>`_). - Fixed installation dependency on virtualenv (`Issue 60 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Fixed the method for determining the Python version so it works under Python 2.4 (`Issue 61 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Converted the test infrastructure to use `tox <http://codespeak.net/tox/index.html>`_ instead of home-grown scripts in the Makefile.

Saturday, March 16, 13

2.6

- Fixed a problem with hook script line endings under Cygwin (`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-versions`) (`Issue 70 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/70>`_). - Fixed installation dependency on virtualenv (`Issue 60 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Fixed the method for determining the Python version so it works under Python 2.4 (`Issue 61 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Converted the test infrastructure to use `tox <http://codespeak.net/tox/index.html>`_ instead of home-grown scripts in the Makefile.

Saturday, March 16, 13

2.6

- Fixed a problem with hook script line endings under Cygwin (`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-versions`) (`Issue 70 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/70>`_). - Fixed installation dependency on virtualenv (`Issue 60 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Fixed the method for determining the Python version so it works under Python 2.4 (`Issue 61 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/60>`_). - Converted the test infrastructure to use `tox <http://codespeak.net/tox/index.html>`_ instead of home-grown scripts in the Makefile.

Saturday, March 16, 13

`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_

Saturday, March 16, 13

`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_

Saturday, March 16, 13

`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_

Saturday, March 16, 13

`68`

`Issue 68 <https://bitbucket.org/dhellmann/virtualenvwrapper/issue/68>`_

:bbissue:

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

from docutils import nodes, utilsfrom docutils.parsers.rst.roles import set_classes

def bbissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): "Link to a BitBucket issue." try: issue_num = int(text) if issue_num <= 0: raise ValueError except ValueError: msg = inliner.reporter.error( '"%s" is an invalid bug id.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app node = make_link_node(rawtext, app, 'issue', text, options) return [node], []

def make_link_node(rawtext, app, type, slug, options): "Create a link to a BitBucket resource." try: base = app.config.bitbucket_project_url if not base: raise AttributeError except AttributeError, err: raise ValueError('bitbucket_project_url not set (%s)' % str(err))

slash = '/' if base[-1] != '/' else '' ref = base + slash + type + '/' + slug + '/' set_classes(options) node = nodes.reference(rawtext, type + ' ' + utils.unescape(slug), refuri=ref, **options) return node

def setup(app): "Install the plugin." app.info('Initializing BitBucket plugin') app.add_role('bbissue', bbissue_role) app.add_config_value('bitbucket_project_url', None, 'env') return

Saturday, March 16, 13

2.6

- Fixed a problem with hook script line endings under Cygwin (:bbissue:`68`). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-versions`) (:bbissue:`70`). - Fixed installation dependency on virtualenv (:bbissue:`60`). - Fixed the method for determining the Python version so it works under Python 2.4 (:bbissue:`61`). - Converted the test infrastructure to use `tox <http://codespeak.net/tox/index.html>`_ instead of home-grown scripts in the Makefile.

Saturday, March 16, 13

2.6

- Fixed a problem with hook script line endings under Cygwin (:bbissue:`68`). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-versions`) (:bbissue:`70`). - Fixed installation dependency on virtualenv (:bbissue:`60`). - Fixed the method for determining the Python version so it works under Python 2.4 (:bbissue:`61`). - Converted the test infrastructure to use `tox <http://codespeak.net/tox/index.html>`_ instead of home-grown scripts in the Makefile.

Saturday, March 16, 13

2.6

• Fixed a problem with hook script line endings under Cygwin (issue 68).

• Updated documentation to include a list of the compatible shells (Supported Shells) and Python versions (Python Versions) (issue 70).

• Fixed installation dependency on virtualenv (issue 60).• Fixed the method for determining the Python version so

it works under Python 2.4 (issue 61).• Converted the test infrastructure to use tox instead of

home-grown scripts in the Makefile.

Saturday, March 16, 13

.. name:: arguments :option: value :option: another-value

body line body line

Saturday, March 16, 13

.. name:: arguments :option: value :option: another-value

body line body line

Saturday, March 16, 13

.. name:: arguments :option: value :option: another-value

body line body line

Saturday, March 16, 13

.. name:: arguments :option: value :option: another-value

body line body line

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

Saturday, March 16, 13

Table Node Hierarchy

Saturday, March 16, 13

Table Node Hierarchy

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec thead

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec thead

row

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec thead

row

entry

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec thead

row

entry

paragraph

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec tbodythead

row

entry

paragraph

table

Saturday, March 16, 13

Table Node Hierarchy

tgroup

colspec tbodythead

row

entry

paragraph

row

entry

paragraph

table

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tgroup

colspec

table

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tgroup

colspec

table

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tgroup

colspec

table

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tgroup

colspec

table

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

thead

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

thead

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

thead

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

thead

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tbody

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tbody

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tbody

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tbody

row

entry

paragraph

Saturday, March 16, 13

from docutils.parsers.rst.directives.tables import Table

class SQLTable(Table):

option_spec = {'widths': directives.positive_int_list, 'class': directives.class_option, 'name': directives.unchanged, 'connection_string':directives.unchanged, }

def run(self): env = self.state.document.settings.env app = env.app config = app.config

# Make sure we have some content, which for now we # assume is a query. if not self.content: error = self.state_machine.reporter.error( 'No query in sqltable directive', nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error]

# Connect to the database connection_string = self.options.get( 'connection_string', config.sqltable_connection_string, ) app.info('Connecting to %s' % connection_string) engine = sqlalchemy.create_engine(connection_string)

# Run the query query = '\n'.join(self.content) app.info('Running query %r' % query) results = engine.execute(query)

# Extract some values we need for building the table. table_headers = results.keys() table_body = results max_cols = len(table_headers) max_header_cols = max_cols

# Handle the width settings and title col_widths = self.get_column_widths(max_cols) title, messages = self.make_title()

# Build the node containing the table content table_node = self.build_table(table_body, col_widths, table_headers) table_node['classes'] += self.options.get('class', []) self.add_name(table_node) if title: table_node.insert(0, title) return [table_node] + messages

def build_table(self, table_data, col_widths, headers): table = nodes.table()

# Set up the column specifications # based on the widths. tgroup = nodes.tgroup(cols=len(col_widths)) table += tgroup tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)

# Set the headers thead = nodes.thead() tgroup += thead row_node = nodes.row() thead += row_node row_node.extend( nodes.entry(h, nodes.paragraph(text=h)) for h in headers )

# The body of the table is made up of rows. # Each row contains a series of entries, # and each entry contains a paragraph of text. tbody = nodes.tbody() tgroup += tbody rows = [] for row in table_data: trow = nodes.row() for cell in row: entry = nodes.entry() para = nodes.paragraph( text=unicode(cell)) entry += para trow += entry rows.append(trow) tbody.extend(rows)

#print table return table

def setup(app): app.info('Initializing SQLTable') app.add_config_value('sqltable_connection_string', '', 'env') app.add_directive('sqltable', SQLTable)

tbody

row

entry

paragraph

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

.. sqltable:: List of Users :connection_string: sqlite:///sampledata.db

select name as 'Name', email as 'E-mail' from users order by Name asc

Saturday, March 16, 13

BuildEnvironment

docutilsparser

SphinxBuilder

SphinxApplication

.rst .pdf .html

Saturday, March 16, 13

Saturday, March 16, 13

.rst

Saturday, March 16, 13

.rst

Saturday, March 16, 13

.rst

wordlist

check

Saturday, March 16, 13

.rst

wordlist

check

Saturday, March 16, 13

section

title paragraph

#text emphasis #text literal

#text#text

#text

Saturday, March 16, 13

section

title paragraph

#text emphasis #text literal

#text#text

#text

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

from sphinx.builders import Builder

class SpellingBuilder(Builder):

name = 'spelling'

def init(self): self.docnames = [] self.document_data = []

project_words = os.path.join(self.srcdir, self.config.spelling_word_list_filename)

self.checker = SpellingChecker( lang=self.config.spelling_lang, suggest=self.config.spelling_show_suggestions, word_list_filename=project_words, ) self.output_filename = os.path.join(self.outdir, 'output.txt') self.output = codecs.open(self.output_filename, 'wt', encoding='UTF-8')

def write_doc(self, docname, doctree):

filename = self.env.doc2path(docname, base=None)

for node in doctree.traverse(docutils.nodes.Text): if (node.tagname == '#text' and node.parent and node.parent.tagname in TEXT_NODES):

# Determine the line number for this node lineno = get_line_number(node)

# Check the text of the node. for word, suggestions in self.checker.check(node.astext()):

msg_parts = [ docname ] if lineno: msg_parts.append( darkgreen('(line %3d)' % lineno) ) msg_parts.append(red(word)) msg_parts.append( self.format_suggestions(suggestions) ) msg = ' '.join(msg_parts) self.info(msg) self.output.write(u"%s:%s: (%s) %s\n" % ( self.env.doc2path(docname, None), lineno, word, self.format_suggestions(suggestions), ))

# Found at least one bad spelling self.app.statuscode = 1

return

def finish(self): self.output.close() self.info('Output written to %s' % self.output_filename) return

def setup(app): app.info('Initializing Spelling Checker') app.add_builder(SpellingBuilder) return

Saturday, March 16, 13

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling

Running Sphinx v1.1.2Initializing Spelling Checkerloading reprint directiveloading books directivesloading pickled environment... donebuilding [spelling]: all documentsupdating environment: 0 added, 0 changed, 0 removedlooking for now-outdated files... none foundpreparing documents... doneindex (line 13) blarg ["blare", "blarney", "Blair", "blamer", "blazer", "blog"] writing output... [100%]Spelling checker messages written to /Users/dhellmann/Devel/website/website/build/spelling/output.txtbuild finished with problems.

Saturday, March 16, 13

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling

Running Sphinx v1.1.2Initializing Spelling Checkerloading reprint directiveloading books directivesloading pickled environment... donebuilding [spelling]: all documentsupdating environment: 0 added, 0 changed, 0 removedlooking for now-outdated files... none foundpreparing documents... doneindex (line 13) blarg ["blare", "blarney", "Blair", "blamer", "blazer", "blog"] writing output... [100%]Spelling checker messages written to /Users/dhellmann/Devel/website/website/build/spelling/output.txtbuild finished with problems.

Saturday, March 16, 13

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling

Running Sphinx v1.1.2Initializing Spelling Checkerloading reprint directiveloading books directivesloading pickled environment... donebuilding [spelling]: all documentsupdating environment: 0 added, 0 changed, 0 removedlooking for now-outdated files... none foundpreparing documents... doneindex (line 13) blarg ["blare", "blarney", "Blair", "blamer", "blazer", "blog"] writing output... [100%]Spelling checker messages written to /Users/dhellmann/Devel/website/website/build/spelling/output.txtbuild finished with problems.

Saturday, March 16, 13

Better Documentation Through Automation:Creating docutils & Sphinx Extensions

Doug Hellmann@doughellmannPyCon 2013

http://docutils.sourceforge.net/https://bitbucket.org/birkenfeld/sphinx-contrib/http://packages.python.org/pyenchant/http://doughellmann.com

Saturday, March 16, 13

Recommended