diff options
author | Stephen Finucane <stephen@that.guru> | 2019-02-08 17:03:09 +0000 |
---|---|---|
committer | Stephen Finucane <stephen@that.guru> | 2019-02-09 17:58:30 +0000 |
commit | 93081e2fce3a7795ddbc23fb4f1df9224f17d3fc (patch) | |
tree | 8f871af32ab165a1f56ecb08e4f2e8914eacbffa /doc/development/tutorials | |
parent | 513d99c316055f63d77aa3c2c1b9ec73b277d426 (diff) | |
download | sphinx-git-93081e2fce3a7795ddbc23fb4f1df9224f17d3fc.tar.gz |
doc: Use 'literalinclude' directive for examples
This avoid duplication and could conceivably let us test this stuff in
code later on.
Signed-off-by: Stephen Finucane <stephen@that.guru>
Diffstat (limited to 'doc/development/tutorials')
-rw-r--r-- | doc/development/tutorials/examples/helloworld.py | 13 | ||||
-rw-r--r-- | doc/development/tutorials/examples/todo.py | 120 | ||||
-rw-r--r-- | doc/development/tutorials/helloworld.rst | 38 | ||||
-rw-r--r-- | doc/development/tutorials/todo.rst | 263 |
4 files changed, 171 insertions, 263 deletions
diff --git a/doc/development/tutorials/examples/helloworld.py b/doc/development/tutorials/examples/helloworld.py new file mode 100644 index 000000000..66ab3295d --- /dev/null +++ b/doc/development/tutorials/examples/helloworld.py @@ -0,0 +1,13 @@ +from docutils import nodes +from docutils.parsers.rst import Directive + + +class HelloWorld(Directive): + + def run(self): + paragraph_node = nodes.paragraph(text='Hello World!') + return [paragraph_node] + + +def setup(app): + app.add_directive("helloworld", HelloWorld) diff --git a/doc/development/tutorials/examples/todo.py b/doc/development/tutorials/examples/todo.py new file mode 100644 index 000000000..bd50ba54a --- /dev/null +++ b/doc/development/tutorials/examples/todo.py @@ -0,0 +1,120 @@ +from docutils import nodes +from docutils.parsers.rst import Directive +from sphinx.locale import _ + + +class todo(nodes.Admonition, nodes.Element): + pass + + +class todolist(nodes.General, nodes.Element): + pass + + +def visit_todo_node(self, node): + self.visit_admonition(node) + + +def depart_todo_node(self, node): + self.depart_admonition(node) + + +class TodolistDirective(Directive): + + def run(self): + return [todolist('')] + + +class TodoDirective(Directive): + + # this enables content in the directive + has_content = True + + def run(self): + env = self.state.document.settings.env + + targetid = 'todo-%d' % env.new_serialno('todo') + targetnode = nodes.target('', '', ids=[targetid]) + + todo_node = todo('\n'.join(self.content)) + todo_node += nodes.title(_('Todo'), _('Todo')) + self.state.nested_parse(self.content, self.content_offset, todo_node) + + if not hasattr(env, 'todo_all_todos'): + env.todo_all_todos = [] + + env.todo_all_todos.append({ + 'docname': env.docname, + 'lineno': self.lineno, + 'todo': todo_node.deepcopy(), + 'target': targetnode, + }) + + return [targetnode, todo_node] + + +def purge_todos(app, env, docname): + if not hasattr(env, 'todo_all_todos'): + return + + env.todo_all_todos = [todo for todo in env.todo_all_todos + if todo['docname'] != docname] + + +def process_todo_nodes(app, doctree, fromdocname): + if not app.config.todo_include_todos: + for node in doctree.traverse(todo): + node.parent.remove(node) + + # Replace all todolist nodes with a list of the collected todos. + # Augment each todo with a backlink to the original location. + env = app.builder.env + + for node in doctree.traverse(todolist): + if not app.config.todo_include_todos: + node.replace_self([]) + continue + + content = [] + + for todo_info in env.todo_all_todos: + para = nodes.paragraph() + filename = env.doc2path(todo_info['docname'], base=None) + description = ( + _('(The original entry is located in %s, line %d and can be found ') % + (filename, todo_info['lineno'])) + para += nodes.Text(description, description) + + # Create a reference + newnode = nodes.reference('', '') + innernode = nodes.emphasis(_('here'), _('here')) + newnode['refdocname'] = todo_info['docname'] + newnode['refuri'] = app.builder.get_relative_uri( + fromdocname, todo_info['docname']) + newnode['refuri'] += '#' + todo_info['target']['refid'] + newnode.append(innernode) + para += newnode + para += nodes.Text('.)', '.)') + + # Insert into the todolist + content.append(todo_info['todo']) + content.append(para) + + node.replace_self(content) + + +def setup(app): + app.add_config_value('todo_include_todos', False, 'html') + + app.add_node(todolist) + app.add_node(todo, + html=(visit_todo_node, depart_todo_node), + latex=(visit_todo_node, depart_todo_node), + text=(visit_todo_node, depart_todo_node)) + + app.add_directive('todo', TodoDirective) + app.add_directive('todolist', TodolistDirective) + app.connect('doctree-resolved', process_todo_nodes) + app.connect('env-purge-doc', purge_todos) + + return {'version': '0.1'} # identifies the version of our extension diff --git a/doc/development/tutorials/helloworld.rst b/doc/development/tutorials/helloworld.rst index b73a12cb0..857b86c9a 100644 --- a/doc/development/tutorials/helloworld.rst +++ b/doc/development/tutorials/helloworld.rst @@ -55,22 +55,9 @@ Writing the extension Open :file:`helloworld.py` and paste the following code in it: -.. code-block:: python - - from docutils import nodes - from docutils.parsers.rst import Directive - - - class HelloWorld(Directive): - - def run(self): - paragraph_node = nodes.paragraph(text='Hello World!') - return [paragraph_node] - - - def setup(app): - app.add_directive("helloworld", HelloWorld) - +.. literalinclude:: examples/helloworld.py + :language: python + :linenos: Some essential things are happening in this example, and you will see them for all directives. @@ -79,13 +66,10 @@ all directives. Our new directive is declared in the ``HelloWorld`` class. -.. code-block:: python - - class HelloWorld(Directive): - - def run(self): - paragraph_node = nodes.paragraph(text='Hello World!') - return [paragraph_node] +.. literalinclude:: examples/helloworld.py + :language: python + :linenos: + :lines: 5-9 This class extends the docutils_' ``Directive`` class. All extensions that create directives should extend this class. @@ -115,10 +99,10 @@ the ``text`` parameter. This function is a requirement. We use it to plug our new directive into Sphinx. -.. code-block:: python - - def setup(app): - app.add_directive("helloworld", HelloWorld) +.. literalinclude:: examples/helloworld.py + :language: python + :linenos: + :lines: 12- The simplest thing you can do it call the :meth:`~Sphinx.add_directive` method, which is what we've done here. For this particular call, the first diff --git a/doc/development/tutorials/todo.rst b/doc/development/tutorials/todo.rst index e3c4861c7..8071bda68 100644 --- a/doc/development/tutorials/todo.rst +++ b/doc/development/tutorials/todo.rst @@ -78,127 +78,9 @@ Writing the extension Open :file:`todo.py` and paste the following code in it, all of which we will explain in detail shortly: -.. code-block:: python - - from docutils import nodes - from docutils.parsers.rst import Directive - from sphinx.locale import _ - - - class todo(nodes.Admonition, nodes.Element): - pass - - - class todolist(nodes.General, nodes.Element): - pass - - - def visit_todo_node(self, node): - self.visit_admonition(node) - - - def depart_todo_node(self, node): - self.depart_admonition(node) - - - class TodolistDirective(Directive): - - def run(self): - return [todolist('')] - - - class TodoDirective(Directive): - - # this enables content in the directive - has_content = True - - def run(self): - env = self.state.document.settings.env - - targetid = 'todo-%d' % env.new_serialno('todo') - targetnode = nodes.target('', '', ids=[targetid]) - - todo_node = todo('\n'.join(self.content)) - todo_node += nodes.title(_('Todo'), _('Todo')) - self.state.nested_parse(self.content, self.content_offset, todo_node) - - if not hasattr(env, 'todo_all_todos'): - env.todo_all_todos = [] - - env.todo_all_todos.append({ - 'docname': env.docname, - 'lineno': self.lineno, - 'todo': todo_node.deepcopy(), - 'target': targetnode, - }) - - return [targetnode, todo_node] - - - def purge_todos(app, env, docname): - if not hasattr(env, 'todo_all_todos'): - return - env.todo_all_todos = [todo for todo in env.todo_all_todos - if todo['docname'] != docname] - - - def process_todo_nodes(app, doctree, fromdocname): - if not app.config.todo_include_todos: - for node in doctree.traverse(todo): - node.parent.remove(node) - - # Replace all todolist nodes with a list of the collected todos. - # Augment each todo with a backlink to the original location. - env = app.builder.env - - for node in doctree.traverse(todolist): - if not app.config.todo_include_todos: - node.replace_self([]) - continue - - content = [] - - for todo_info in env.todo_all_todos: - para = nodes.paragraph() - filename = env.doc2path(todo_info['docname'], base=None) - description = ( - _('(The original entry is located in %s, line %d and can be found ') % - (filename, todo_info['lineno'])) - para += nodes.Text(description, description) - - # Create a reference - newnode = nodes.reference('', '') - innernode = nodes.emphasis(_('here'), _('here')) - newnode['refdocname'] = todo_info['docname'] - newnode['refuri'] = app.builder.get_relative_uri( - fromdocname, todo_info['docname']) - newnode['refuri'] += '#' + todo_info['target']['refid'] - newnode.append(innernode) - para += newnode - para += nodes.Text('.)', '.)') - - # Insert into the todolist - content.append(todo_info['todo']) - content.append(para) - - node.replace_self(content) - - - def setup(app): - app.add_config_value('todo_include_todos', False, 'html') - - app.add_node(todolist) - app.add_node(todo, - html=(visit_todo_node, depart_todo_node), - latex=(visit_todo_node, depart_todo_node), - text=(visit_todo_node, depart_todo_node)) - - app.add_directive('todo', TodoDirective) - app.add_directive('todolist', TodolistDirective) - app.connect('doctree-resolved', process_todo_nodes) - app.connect('env-purge-doc', purge_todos) - - return {'version': '0.1'} # identifies the version of our extension +.. literalinclude:: examples/todo.py + :language: python + :linenos: This is far more extensive extension than the one detailed in :doc:`helloworld`, however, we will will look at each piece step-by-step to explain what's @@ -208,21 +90,10 @@ happening. Let's start with the node classes: -.. todo:: Use literal-include - -.. code-block:: python - - class todo(nodes.Admonition, nodes.Element): - pass - - class todolist(nodes.General, nodes.Element): - pass - - def visit_todo_node(self, node): - self.visit_admonition(node) - - def depart_todo_node(self, node): - self.depart_admonition(node) +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 6-19 Node classes usually don't have to do anything except inherit from the standard docutils classes defined in :mod:`docutils.nodes`. ``todo`` inherits from @@ -246,46 +117,20 @@ the class should have attributes that configure the allowed markup, and a Looking first at the ``TodolistDirective`` directive: -.. code-block:: python - - class TodolistDirective(Directive): - - def run(self): - return [todolist('')] +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 22-25 It's very simple, creating and returning an instance of our ``todolist`` node class. The ``TodolistDirective`` directive itself has neither content nor arguments that need to be handled. That brings us to the ``TodoDirective`` directive: -.. code-block:: python - - class TodoDirective(Directive): - - # this enables content in the directive - has_content = True - - def run(self): - env = self.state.document.settings.env - - targetid = "todo-%d" % env.new_serialno('todo') - targetnode = nodes.target('', '', ids=[targetid]) - - todo_node = todo('\n'.join(self.content)) - todo_node += nodes.title(_('Todo'), _('Todo')) - self.state.nested_parse(self.content, self.content_offset, todo_node) - - if not hasattr(env, 'todo_all_todos'): - env.todo_all_todos = [] - - env.todo_all_todos.append({ - 'docname': env.docname, - 'lineno': self.lineno, - 'todo': todo_node.deepcopy(), - 'target': targetnode, - }) - - return [targetnode, todo_node] +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 28-53 Several important things are covered here. First, as you can see, you can refer to the :ref:`build environment instance <important-objects>` using @@ -337,13 +182,10 @@ here. Let's look at the event handlers used in the above example. First, the one for the :event:`env-purge-doc` event: -.. code-block:: python - - def purge_todos(app, env, docname): - if not hasattr(env, 'todo_all_todos'): - return - env.todo_all_todos = [todo for todo in env.todo_all_todos - if todo['docname'] != docname] +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 56-61 Since we store information from source files in the environment, which is persistent, it may become out of date when the source file changes. Therefore, @@ -355,48 +197,10 @@ added again during parsing. The other handler belongs to the :event:`doctree-resolved` event: -.. code-block:: python - - def process_todo_nodes(app, doctree, fromdocname): - if not app.config.todo_include_todos: - for node in doctree.traverse(todo): - node.parent.remove(node) - - # Replace all todolist nodes with a list of the collected todos. - # Augment each todo with a backlink to the original location. - env = app.builder.env - - for node in doctree.traverse(todolist): - if not app.config.todo_include_todos: - node.replace_self([]) - continue - - content = [] - - for todo_info in env.todo_all_todos: - para = nodes.paragraph() - filename = env.doc2path(todo_info['docname'], base=None) - description = ( - _('(The original entry is located in %s, line %d and can be found ') % - (filename, todo_info['lineno'])) - para += nodes.Text(description, description) - - # Create a reference - newnode = nodes.reference('', '') - innernode = nodes.emphasis(_('here'), _('here')) - newnode['refdocname'] = todo_info['docname'] - newnode['refuri'] = app.builder.get_relative_uri( - fromdocname, todo_info['docname']) - newnode['refuri'] += '#' + todo_info['target']['refid'] - newnode.append(innernode) - para += newnode - para += nodes.Text('.)', '.)') - - # Insert into the todolist - content.append(todo_info['todo']) - content.append(para) - - node.replace_self(content) +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 64-103 The :event:`doctree-resolved` event is emitted at the end of :ref:`phase 3 <build-phases>` and allows custom resolving to be done. The handler we have @@ -420,23 +224,10 @@ As noted :doc:`previously <helloworld>`, the ``setup`` function is a requirement and is used to plug directives into Sphinx. However, we also use it to hook up the other parts of our extension. Let's look at our ``setup`` function: -.. code-block:: python - - def setup(app): - app.add_config_value('todo_include_todos', False, 'html') - - app.add_node(todolist) - app.add_node(todo, - html=(visit_todo_node, depart_todo_node), - latex=(visit_todo_node, depart_todo_node), - text=(visit_todo_node, depart_todo_node)) - - app.add_directive('todo', TodoDirective) - app.add_directive('todolist', TodolistDirective) - app.connect('doctree-resolved', process_todo_nodes) - app.connect('env-purge-doc', purge_todos) - - return {'version': '0.1'} # identifies the version of our extension +.. literalinclude:: examples/todo.py + :language: python + :linenos: + :lines: 106- The calls in this function refer to the classes and functions we added earlier. What the individual calls do is the following: |