Slide 1

Slide 1 text

Better Documentation Through Automation: Creating docutils & Sphinx Extensions Doug Hellmann @doughellmann PyCon 2013 Saturday, March 16, 13

Slide 2

Slide 2 text

Markup Language Saturday, March 16, 13

Slide 3

Slide 3 text

Markup Language Extensible Saturday, March 16, 13

Slide 4

Slide 4 text

Saturday, March 16, 13

Slide 5

Slide 5 text

Sphinx Application Saturday, March 16, 13

Slide 6

Slide 6 text

Build Environment Sphinx Application .rst Saturday, March 16, 13

Slide 7

Slide 7 text

Build Environment docutils parser Sphinx Application .rst Saturday, March 16, 13

Slide 8

Slide 8 text

Build Environment docutils parser Sphinx Builder Sphinx Application .rst .pdf .html Saturday, March 16, 13

Slide 9

Slide 9 text

section title paragraph #text emphasis #text literal #text #text #text Saturday, March 16, 13

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

.. 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

Slide 13

Slide 13 text

2.6 - Fixed a problem with hook script line endings under Cygwin (`Issue 68 `_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-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

Slide 14

Slide 14 text

2.6 - Fixed a problem with hook script line endings under Cygwin (`Issue 68 `_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-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

Slide 15

Slide 15 text

2.6 - Fixed a problem with hook script line endings under Cygwin (`Issue 68 `_). - Updated documentation to include a list of the compatible shells (:ref:`supported-shells`) and Python versions (:ref:`supported-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

Slide 16

Slide 16 text

`Issue 68 `_ Saturday, March 16, 13

Slide 17

Slide 17 text

`Issue 68 `_ Saturday, March 16, 13

Slide 18

Slide 18 text

`Issue 68 `_ Saturday, March 16, 13

Slide 19

Slide 19 text

`68` `Issue 68 `_ :bbissue: Saturday, March 16, 13

Slide 20

Slide 20 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 21

Slide 21 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 22

Slide 22 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 23

Slide 23 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 24

Slide 24 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 25

Slide 25 text

from docutils import nodes, utils from 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: Saturday, March 16, 13

Slide 26

Slide 26 text

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) Saturday, March 16, 13

Slide 27

Slide 27 text

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) Saturday, March 16, 13

Slide 28

Slide 28 text

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) Saturday, March 16, 13

Slide 29

Slide 29 text

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) Saturday, March 16, 13

Slide 30

Slide 30 text

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) Saturday, March 16, 13

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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 `_ instead of home-grown scripts in the Makefile. Saturday, March 16, 13

Slide 35

Slide 35 text

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 `_ instead of home-grown scripts in the Makefile. Saturday, March 16, 13

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

.. name:: arguments :option: value :option: another-value body line body line Saturday, March 16, 13

Slide 38

Slide 38 text

.. name:: arguments :option: value :option: another-value body line body line Saturday, March 16, 13

Slide 39

Slide 39 text

.. name:: arguments :option: value :option: another-value body line body line Saturday, March 16, 13

Slide 40

Slide 40 text

.. name:: arguments :option: value :option: another-value body line body line Saturday, March 16, 13

Slide 41

Slide 41 text

.. 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

Slide 42

Slide 42 text

.. 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

Slide 43

Slide 43 text

.. 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

Slide 44

Slide 44 text

.. 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

Slide 45

Slide 45 text

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 Saturday, March 16, 13

Slide 46

Slide 46 text

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 Saturday, March 16, 13

Slide 47

Slide 47 text

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 Saturday, March 16, 13

Slide 48

Slide 48 text

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 Saturday, March 16, 13

Slide 49

Slide 49 text

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 Saturday, March 16, 13

Slide 50

Slide 50 text

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 Saturday, March 16, 13

Slide 51

Slide 51 text

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 Saturday, March 16, 13

Slide 52

Slide 52 text

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 Saturday, March 16, 13

Slide 53

Slide 53 text

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) Saturday, March 16, 13

Slide 54

Slide 54 text

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) Saturday, March 16, 13

Slide 55

Slide 55 text

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) Saturday, March 16, 13

Slide 56

Slide 56 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 Saturday, March 16, 13

Slide 57

Slide 57 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 Saturday, March 16, 13

Slide 58

Slide 58 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 Saturday, March 16, 13

Slide 59

Slide 59 text

# 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): Saturday, March 16, 13

Slide 60

Slide 60 text

# 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): Saturday, March 16, 13

Slide 61

Slide 61 text

# 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): Saturday, March 16, 13

Slide 62

Slide 62 text

# 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): Saturday, March 16, 13

Slide 63

Slide 63 text

# 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() Saturday, March 16, 13

Slide 64

Slide 64 text

# 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() Saturday, March 16, 13

Slide 65

Slide 65 text

# 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() Saturday, March 16, 13

Slide 66

Slide 66 text

Table Node Hierarchy Saturday, March 16, 13

Slide 67

Slide 67 text

Table Node Hierarchy table Saturday, March 16, 13

Slide 68

Slide 68 text

Table Node Hierarchy tgroup table Saturday, March 16, 13

Slide 69

Slide 69 text

Table Node Hierarchy tgroup colspec table Saturday, March 16, 13

Slide 70

Slide 70 text

Table Node Hierarchy tgroup colspec thead table Saturday, March 16, 13

Slide 71

Slide 71 text

Table Node Hierarchy tgroup colspec thead row table Saturday, March 16, 13

Slide 72

Slide 72 text

Table Node Hierarchy tgroup colspec thead row entry table Saturday, March 16, 13

Slide 73

Slide 73 text

Table Node Hierarchy tgroup colspec thead row entry paragraph table Saturday, March 16, 13

Slide 74

Slide 74 text

Table Node Hierarchy tgroup colspec tbody thead row entry paragraph table Saturday, March 16, 13

Slide 75

Slide 75 text

Table Node Hierarchy tgroup colspec tbody thead row entry paragraph row entry paragraph table Saturday, March 16, 13

Slide 76

Slide 76 text

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 tgroup colspec table Saturday, March 16, 13

Slide 77

Slide 77 text

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 tgroup colspec table Saturday, March 16, 13

Slide 78

Slide 78 text

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 tgroup colspec table Saturday, March 16, 13

Slide 79

Slide 79 text

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 tgroup colspec table Saturday, March 16, 13

Slide 80

Slide 80 text

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 thead row entry paragraph Saturday, March 16, 13

Slide 81

Slide 81 text

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 thead row entry paragraph Saturday, March 16, 13

Slide 82

Slide 82 text

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 thead row entry paragraph Saturday, March 16, 13

Slide 83

Slide 83 text

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 thead row entry paragraph Saturday, March 16, 13

Slide 84

Slide 84 text

) # 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') tbody row entry paragraph Saturday, March 16, 13

Slide 85

Slide 85 text

) # 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') tbody row entry paragraph Saturday, March 16, 13

Slide 86

Slide 86 text

) # 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') tbody row entry paragraph Saturday, March 16, 13

Slide 87

Slide 87 text

) # 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') tbody row entry paragraph Saturday, March 16, 13

Slide 88

Slide 88 text

) # 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') tbody row entry paragraph Saturday, March 16, 13

Slide 89

Slide 89 text

.. 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

Slide 90

Slide 90 text

.. 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

Slide 91

Slide 91 text

.. 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

Slide 92

Slide 92 text

.. 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

Slide 93

Slide 93 text

Build Environment docutils parser Sphinx Builder Sphinx Application .rst .pdf .html Saturday, March 16, 13

Slide 94

Slide 94 text

Saturday, March 16, 13

Slide 95

Slide 95 text

.rst Saturday, March 16, 13

Slide 96

Slide 96 text

.rst Saturday, March 16, 13

Slide 97

Slide 97 text

.rst word list check Saturday, March 16, 13

Slide 98

Slide 98 text

.rst word list check Saturday, March 16, 13

Slide 99

Slide 99 text

section title paragraph #text emphasis #text literal #text #text #text Saturday, March 16, 13

Slide 100

Slide 100 text

section title paragraph #text emphasis #text literal #text #text #text Saturday, March 16, 13

Slide 101

Slide 101 text

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): Saturday, March 16, 13

Slide 102

Slide 102 text

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): Saturday, March 16, 13

Slide 103

Slide 103 text

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): Saturday, March 16, 13

Slide 104

Slide 104 text

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): Saturday, March 16, 13

Slide 105

Slide 105 text

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): Saturday, March 16, 13

Slide 106

Slide 106 text

'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) ) Saturday, March 16, 13

Slide 107

Slide 107 text

'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) ) Saturday, March 16, 13

Slide 108

Slide 108 text

'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) ) Saturday, March 16, 13

Slide 109

Slide 109 text

'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) ) Saturday, March 16, 13

Slide 110

Slide 110 text

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 Saturday, March 16, 13

Slide 111

Slide 111 text

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 Saturday, March 16, 13

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling Running Sphinx v1.1.2 Initializing Spelling Checker loading reprint directive loading books directives loading pickled environment... done building [spelling]: all documents updating environment: 0 added, 0 changed, 0 removed looking for now-outdated files... none found preparing documents... done index (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.txt build finished with problems. Saturday, March 16, 13

Slide 116

Slide 116 text

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling Running Sphinx v1.1.2 Initializing Spelling Checker loading reprint directive loading books directives loading pickled environment... done building [spelling]: all documents updating environment: 0 added, 0 changed, 0 removed looking for now-outdated files... none found preparing documents... done index (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.txt build finished with problems. Saturday, March 16, 13

Slide 117

Slide 117 text

$ sphinx-build -b spelling -d build/doctrees \ source build/spelling Running Sphinx v1.1.2 Initializing Spelling Checker loading reprint directive loading books directives loading pickled environment... done building [spelling]: all documents updating environment: 0 added, 0 changed, 0 removed looking for now-outdated files... none found preparing documents... done index (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.txt build finished with problems. Saturday, March 16, 13

Slide 118

Slide 118 text

Better Documentation Through Automation: Creating docutils & Sphinx Extensions Doug Hellmann @doughellmann PyCon 2013 http://docutils.sourceforge.net/ https://bitbucket.org/birkenfeld/sphinx-contrib/ http://packages.python.org/pyenchant/ http://doughellmann.com Saturday, March 16, 13