diff options
author | Stephen Finucane <stephen@that.guru> | 2019-02-11 10:44:49 +0000 |
---|---|---|
committer | Stephen Finucane <stephen@that.guru> | 2019-02-14 10:31:45 +0000 |
commit | 5c061ff2665f7177b110416b49b5dd37aadeda5b (patch) | |
tree | b2ff367b0d33d08d96060e148816bf75da7175b3 /doc/development/tutorials | |
parent | b1ebdcd3c68cb3942ad407b39c994b3fa05ad257 (diff) | |
download | sphinx-git-5c061ff2665f7177b110416b49b5dd37aadeda5b.tar.gz |
docs: Address review comments
helloworld:
- Return version metadata from extension
- Use 'reST' instead of 'rST'
- Don't use single backticks
todo:
- Return version metadata from extension
- Link to events section of API guide, rather than entire document
- Include name of phases in when describing build phases
- Use more method cross-references where possible
- Fix typo in usage example
recipe:
- Return version metadata from extension
- Rework code to simplify things
- Remove docstrings from 'generate' functions, which are simply
duplicates of the original docstring
- Rename 'rcp' directive to 'recipe', the 'reref' role to 'ref', and a
whole lot of variables to something more grokable
- Fix typos in both documentation and code
I've also fixed up the docstrings for some of the 'domain' functions to
make them render a little nicer (they took me a while to figure out).
Signed-off-by: Stephen Finucane <stephen@that.guru>
Diffstat (limited to 'doc/development/tutorials')
-rw-r--r-- | doc/development/tutorials/examples/README.rst | 11 | ||||
-rw-r--r-- | doc/development/tutorials/examples/helloworld.py | 6 | ||||
-rw-r--r-- | doc/development/tutorials/examples/recipe.py | 228 | ||||
-rw-r--r-- | doc/development/tutorials/examples/todo.py | 6 | ||||
-rw-r--r-- | doc/development/tutorials/helloworld.rst | 17 | ||||
-rw-r--r-- | doc/development/tutorials/recipe.rst | 85 | ||||
-rw-r--r-- | doc/development/tutorials/todo.rst | 43 |
7 files changed, 174 insertions, 222 deletions
diff --git a/doc/development/tutorials/examples/README.rst b/doc/development/tutorials/examples/README.rst new file mode 100644 index 000000000..2b9c01b5e --- /dev/null +++ b/doc/development/tutorials/examples/README.rst @@ -0,0 +1,11 @@ +:orphan: + +Tutorial examples +================= + +This directory contains a number of examples used in the tutorials. These are +intended to be increasingly complex to demonstrate the various features of +Sphinx, but should aim to be as complicated as necessary but no more. +Individual sections are referenced by line numbers, meaning if you make changes +to the source files, you should update the references in the documentation +accordingly. diff --git a/doc/development/tutorials/examples/helloworld.py b/doc/development/tutorials/examples/helloworld.py index 66ab3295d..d6d81fd4f 100644 --- a/doc/development/tutorials/examples/helloworld.py +++ b/doc/development/tutorials/examples/helloworld.py @@ -11,3 +11,9 @@ class HelloWorld(Directive): def setup(app): app.add_directive("helloworld", HelloWorld) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/doc/development/tutorials/examples/recipe.py b/doc/development/tutorials/examples/recipe.py index 213d30ff6..9c54a93f0 100644 --- a/doc/development/tutorials/examples/recipe.py +++ b/doc/development/tutorials/examples/recipe.py @@ -1,3 +1,5 @@ +from collections import defaultdict + import docutils from docutils import nodes from docutils.parsers import rst @@ -18,33 +20,27 @@ class RecipeDirective(ObjectDescription): has_content = True required_arguments = 1 option_spec = { - 'contains': directives.unchanged_required + 'contains': directives.unchanged_required, } def handle_signature(self, sig, signode): signode += addnodes.desc_name(text=sig) - signode += addnodes.desc_type(text='Recipe') return sig def add_target_and_index(self, name_cls, sig, signode): signode['ids'].append('recipe' + '-' + sig) if 'noindex' not in self.options: - name = '{}.{}.{}'.format('rcp', type(self).__name__, sig) - imap = self.env.domaindata['rcp']['obj2ingredient'] - imap[name] = list(self.options.get('contains').split(' ')) - objs = self.env.domaindata['rcp']['objects'] - objs.append((name, - sig, - 'Recipe', - self.env.docname, - 'recipe' + '-' + sig, - 0)) + ingredients = [ + x.strip() for x in self.options.get('contains').split(',')] + + recipes = self.env.get_domain('recipe') + recipes.add_recipe(sig, ingredients) class IngredientIndex(Index): - """A custom directive that creates an ingredient matrix.""" + """A custom index that creates an ingredient matrix.""" - name = 'ing' + name = 'ingredient' localname = 'Ingredient Index' shortname = 'Ingredient' @@ -52,69 +48,39 @@ class IngredientIndex(Index): super(IngredientIndex, self).__init__(*args, **kwargs) def generate(self, docnames=None): - """Return entries for the index given by *name*. - - If *docnames* is given, restrict to entries referring to these - docnames. The return value is a tuple of ``(content, collapse)``, - where: - - *collapse* is a boolean that determines if sub-entries should - start collapsed (for output formats that support collapsing - sub-entries). - - *content* is a sequence of ``(letter, entries)`` tuples, where *letter* - is the "heading" for the given *entries*, usually the starting letter. - - *entries* is a sequence of single entries, where a single entry is a - sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``. - - The items in this sequence have the following meaning: - - - `name` -- the name of the index entry to be displayed - - `subtype` -- sub-entry related type: - - ``0`` -- normal entry - - ``1`` -- entry with sub-entries - - ``2`` -- sub-entry - - `docname` -- docname where the entry is located - - `anchor` -- anchor for the entry within `docname` - - `extra` -- extra info for the entry - - `qualifier` -- qualifier for the description - - `descr` -- description for the entry - - Qualifier and description are not rendered by some builders, such as - the LaTeX builder. - """ - - content = {} - - objs = {name: (dispname, typ, docname, anchor) - for name, dispname, typ, docname, anchor, prio - in self.domain.get_objects()} - - imap = {} - ingr = self.domain.data['obj2ingredient'] - for name, ingr in ingr.items(): - for ig in ingr: - imap.setdefault(ig,[]) - imap[ig].append(name) - - for ingredient in imap.keys(): - lis = content.setdefault(ingredient, []) - objlis = imap[ingredient] - for objname in objlis: - dispname, typ, docname, anchor = objs[objname] - lis.append(( - dispname, 0, docname, - anchor, - docname, '', typ - )) - re = [(k, v) for k, v in sorted(content.items())] - - return (re, True) + content = defaultdict(list) + + recipes = {name: (dispname, typ, docname, anchor) + for name, dispname, typ, docname, anchor, _ + in self.domain.get_objects()} + recipe_ingredients = self.domain.data['recipe_ingredients'] + ingredient_recipes = defaultdict(list) + + # flip from recipe_ingredients to ingredient_recipes + for recipe_name, ingredients in recipe_ingredients.items(): + for ingredient in ingredients: + ingredient_recipes[ingredient].append(recipe_name) + + # convert the mapping of ingredient to recipes to produce the expected + # output, shown below, using the ingredient name as a key to group + # + # name, subtype, docname, anchor, extra, qualifier, description + for ingredient, recipe_names in ingredient_recipes.items(): + for recipe_name in recipe_names: + dispname, typ, docname, anchor = recipes[recipe_name] + content[ingredient].append( + (dispname, 0, docname, anchor, docname, '', typ)) + + # convert the dict to the sorted list of tuples expected + content = sorted(content.items()) + + return content, True class RecipeIndex(Index): - name = 'rcp' + """A custom index that creates an recipe matrix.""" + + name = 'recipe' localname = 'Recipe Index' shortname = 'Recipe' @@ -122,92 +88,54 @@ class RecipeIndex(Index): super(RecipeIndex, self).__init__(*args, **kwargs) def generate(self, docnames=None): - """Return entries for the index given by *name*. - - If *docnames* is given, restrict to entries referring to these - docnames. The return value is a tuple of ``(content, collapse)``, - where: - - *collapse* is a boolean that determines if sub-entries should - start collapsed (for output formats that support collapsing - sub-entries). - - *content* is a sequence of ``(letter, entries)`` tuples, where *letter* - is the "heading" for the given *entries*, usually the starting letter. - - *entries* is a sequence of single entries, where a single entry is a - sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``. - - The items in this sequence have the following meaning: - - - `name` -- the name of the index entry to be displayed - - `subtype` -- sub-entry related type: - - ``0`` -- normal entry - - ``1`` -- entry with sub-entries - - ``2`` -- sub-entry - - `docname` -- docname where the entry is located - - `anchor` -- anchor for the entry within `docname` - - `extra` -- extra info for the entry - - `qualifier` -- qualifier for the description - - `descr` -- description for the entry - - Qualifier and description are not rendered by some builders, such as - the LaTeX builder. - """ - - content = {} - items = ((name, dispname, typ, docname, anchor) - for name, dispname, typ, docname, anchor, prio - in self.domain.get_objects()) - items = sorted(items, key=lambda item: item[0]) - for name, dispname, typ, docname, anchor in items: - lis = content.setdefault('Recipe', []) - lis.append(( - dispname, 0, docname, - anchor, - docname, '', typ - )) - re = [(k, v) for k, v in sorted(content.items())] - - return (re, True) + content = defaultdict(list) + + # sort the list of recipes in alphabetical order + recipes = self.domain.get_objects() + recipes = sorted(recipes, key=lambda recipe: recipe[0]) + + # generate the expected output, shown below, from the above using the + # first letter of the recipe as a key to group thing + # + # name, subtype, docname, anchor, extra, qualifier, description + for name, dispname, typ, docname, anchor, _ in recipes: + content[dispname[0].lower()].append( + (dispname, 0, docname, anchor, docname, '', typ)) + + # convert the dict to the sorted list of tuples expected + content = sorted(content.items()) + + return content, True class RecipeDomain(Domain): - name = 'rcp' + name = 'recipe' label = 'Recipe Sample' - roles = { - 'reref': XRefRole() + 'ref': XRefRole() } - directives = { 'recipe': RecipeDirective, } - indices = { RecipeIndex, IngredientIndex } - initial_data = { - 'objects': [], # object list - 'obj2ingredient': {}, # name -> object + 'recipes': [], # object list + 'recipe_ingredients': {}, # name -> object } def get_full_qualified_name(self, node): - """Return full qualified name for a given node""" - return "{}.{}.{}".format('rcp', - type(node).__name__, - node.arguments[0]) + return '{}.{}'.format('recipe', node.arguments[0]) def get_objects(self): - for obj in self.data['objects']: + for obj in self.data['recipes']: yield(obj) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - match = [(docname, anchor) for name, sig, typ, docname, anchor, prio in self.get_objects() if sig == target] @@ -219,21 +147,25 @@ class RecipeDomain(Domain): return make_refnode(builder, fromdocname, todocname, targ, contnode, targ) else: - print("Awww, found nothing") + print('Awww, found nothing') return None + def add_recipe(self, signature, ingredients): + """Add a new recipe to the domain.""" + name = '{}.{}'.format('recipe', signature) + anchor = 'recipe-{}'.format(signature) -def setup(app): - app.add_domain(RecipeDomain) + self.data['recipe_ingredients'][name] = ingredients + # name, dispname, type, docname, anchor, priority + self.data['recipes'].append( + (name, signature, 'Recipe', self.env.docname, anchor, 0)) - StandardDomain.initial_data['labels']['recipeindex'] = ( - 'rcp-rcp', '', 'Recipe Index') - StandardDomain.initial_data['labels']['ingredientindex'] = ( - 'rcp-ing', '', 'Ingredient Index') - StandardDomain.initial_data['anonlabels']['recipeindex'] = ( - 'rcp-rcp', '') - StandardDomain.initial_data['anonlabels']['ingredientindex'] = ( - 'rcp-ing', '') +def setup(app): + app.add_domain(RecipeDomain) - return {'version': '0.1'} # identifies the version of our extension + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/doc/development/tutorials/examples/todo.py b/doc/development/tutorials/examples/todo.py index bd50ba54a..d46f90821 100644 --- a/doc/development/tutorials/examples/todo.py +++ b/doc/development/tutorials/examples/todo.py @@ -117,4 +117,8 @@ def setup(app): app.connect('doctree-resolved', process_todo_nodes) app.connect('env-purge-doc', purge_todos) - return {'version': '0.1'} # identifies the version of our extension + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/doc/development/tutorials/helloworld.rst b/doc/development/tutorials/helloworld.rst index 857b86c9a..a042f7b05 100644 --- a/doc/development/tutorials/helloworld.rst +++ b/doc/development/tutorials/helloworld.rst @@ -2,12 +2,13 @@ Developing a "Hello world" extension ==================================== The objective of this tutorial is to create a very basic extension that adds a -new directive. This directive will output a paragraph containing `hello world`. +new directive. This directive will output a paragraph containing "hello world". Only basic information is provided in this tutorial. For more information, refer to the :doc:`other tutorials <index>` that go into more details. .. warning:: + For this extension, you will need some basic understanding of docutils_ and Python. @@ -17,7 +18,7 @@ Overview We want the extension to add the following to Sphinx: -* A ``helloworld`` directive, that will simply output the text `hello world`. +* A ``helloworld`` directive, that will simply output the text "hello world". Prerequisites @@ -104,10 +105,10 @@ Sphinx. :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 -argument is the name of the directive itself as used in an rST file. In this -case, we would use ``helloworld``. For example: +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 argument is +the name of the directive itself as used in a reST file. In this case, we would +use ``helloworld``. For example: .. code-block:: rst @@ -117,6 +118,10 @@ case, we would use ``helloworld``. For example: Some more text here... +We also return the :ref:`extension metadata <ext-metadata>` that indicates the +version of our extension, along with the fact that it is safe to use the +extension for both parallel reading and writing. + Using the extension ------------------- diff --git a/doc/development/tutorials/recipe.rst b/doc/development/tutorials/recipe.rst index 67841c3a9..25a2c0732 100644 --- a/doc/development/tutorials/recipe.rst +++ b/doc/development/tutorials/recipe.rst @@ -19,14 +19,14 @@ Overview We want the extension to add the following to Sphinx: * A ``recipe`` :term:`directive`, containing some content describing the recipe - steps, along with a ``:contains:`` argument highlighting the main ingredients + steps, along with a ``:contains:`` option highlighting the main ingredients of the recipe. -* A ``reref`` :term:`role`, which provides a cross-reference to the recipe +* A ``ref`` :term:`role`, which provides a cross-reference to the recipe itself. -* A ``rcp`` :term:`domain`, which allows us to tie together the above role and - domain, along with things like indices. +* A ``recipe`` :term:`domain`, which allows us to tie together the above role + and domain, along with things like indices. For that, we will need to add the following elements to Sphinx: @@ -34,24 +34,15 @@ For that, we will need to add the following elements to Sphinx: * New indexes to allow us to reference ingredient and recipes -* A new domain called ``rcp``, which will contain the ``recipe`` directive and - ``reref`` role +* A new domain called ``recipe``, which will contain the ``recipe`` directive + and ``ref`` role Prerequisites ------------- -As with :doc:`the previous extensions <todo>`, we will not be distributing this -plugin via PyPI so once again we need a Sphinx project to call this from. You -can use an existing project or create a new one using -:program:`sphinx-quickstart`. - -We assume you are using separate source (:file:`source`) and build -(:file:`build`) folders. Your extension file could be in any folder of your -project. In our case, let's do the following: - -#. Create an :file:`_ext` folder in :file:`source` -#. Create a new Python file in the :file:`_ext` folder called :file:`recipe.py` +We need the same setup as in :doc:`the previous extensions <todo>`. This time, +we will be putting out extension in a file called :file:`recipe.py`. Here is an example of the folder structure you might obtain: @@ -59,7 +50,7 @@ Here is an example of the folder structure you might obtain: └── source ├── _ext - │ └── todo.py + │ └── recipe.py ├── conf.py └── index.rst @@ -67,8 +58,8 @@ Here is an example of the folder structure you might obtain: Writing the extension --------------------- -Open :file:`receipe.py` and paste the following code in it, all of which we -will explain in detail shortly: +Open :file:`recipe.py` and paste the following code in it, all of which we will +explain in detail shortly: .. literalinclude:: examples/recipe.py :language: python @@ -79,12 +70,12 @@ on. .. rubric:: The directive class -The first thing to examine is the ``RecipeNode`` directive: +The first thing to examine is the ``RecipeDirective`` directive: .. literalinclude:: examples/recipe.py :language: python :linenos: - :lines: 15-40 + :lines: 17-37 Unlike :doc:`helloworld` and :doc:`todo`, this directive doesn't derive from :class:`docutils.parsers.rst.Directive` and doesn't define a ``run`` method. @@ -100,7 +91,7 @@ for this node. We also see that this directive defines ``has_content``, ``required_arguments`` and ``option_spec``. Unlike the ``TodoDirective`` directive added in the :doc:`previous tutorial <todo>`, this directive takes a single argument, the -recipe name, and an optional argument, ``contains``, in addition to the nested +recipe name, and an option, ``contains``, in addition to the nested reStructuredText in the body. .. rubric:: The index classes @@ -112,11 +103,11 @@ reStructuredText in the body. .. literalinclude:: examples/recipe.py :language: python :linenos: - :lines: 44-172 + :lines: 40-108 Both ``IngredientIndex`` and ``RecipeIndex`` are derived from :class:`Index`. They implement custom logic to generate a tuple of values that define the -index. Note that ``RecipeIndex`` is a degenerate index that has only one entry. +index. Note that ``RecipeIndex`` is a simple index that has only one entry. Extending it to cover more object types is not yet part of the code. Both indices use the method :meth:`Index.generate` to do their work. This @@ -135,9 +126,9 @@ creating here. .. literalinclude:: examples/recipe.py :language: python :linenos: - :lines: 175-223 + :lines: 111-161 -There are some interesting things to note about this ``rcp`` domain and domains +There are some interesting things to note about this ``recipe`` domain and domains in general. Firstly, we actually register our directives, roles and indices here, via the ``directives``, ``roles`` and ``indices`` attributes, rather than via calls later on in ``setup``. We can also note that we aren't actually @@ -146,19 +137,21 @@ defining a custom role and are instead reusing the :class:`sphinx.domains.Domain.resolve_xref` method. This method takes two arguments, ``typ`` and ``target``, which refer to the cross-reference type and its target name. We'll use ``target`` to resolve our destination from our -domain's ``objects`` because we currently have only one type of node. - -Moving on, we can see that we've defined two items in ``intitial_data``: -``objects`` and ``obj2ingredient``. These contain a list of all objects defined -(i.e. all recipes) and a hash that maps a canonical ingredient name to the list -of objects. The way we name objects is common across our extension and is -defined in the ``get_full_qualified_name`` method. For each object created, the -canonical name is ``rcp.<typename>.<objectname>``, where ``<typename>`` is the -Python type of the object, and ``<objectname>`` is the name the documentation -writer gives the object. This enables the extension to use different object -types that share the same name. Having a canonical name and central place for -our objects is a huge advantage. Both our indices and our cross-referencing -code use this feature. +domain's ``recipes`` because we currently have only one type of node. + +Moving on, we can see that we've defined ``initial_data``. The values defined in +``initial_data`` will be copied to ``env.domaindata[domain_name]`` as the +initial data of the domain, and domain instances can access it via +``self.data``. We see that we have defined two items in ``initial_data``: +``recipes`` and ``recipe2ingredient``. These contain a list of all objects +defined (i.e. all recipes) and a hash that maps a canonical ingredient name to +the list of objects. The way we name objects is common across our extension and +is defined in the ``get_full_qualified_name`` method. For each object created, +the canonical name is ``recipe.<recipename>``, where ``<recipename>`` is the +name the documentation writer gives the object (a recipe). This enables the +extension to use different object types that share the same name. Having a +canonical name and central place for our objects is a huge advantage. Both our +indices and our cross-referencing code use this feature. .. rubric:: The ``setup`` function @@ -171,7 +164,7 @@ hook the various parts of our extension into Sphinx. Let's look at the .. literalinclude:: examples/recipe.py :language: python :linenos: - :lines: 226- + :lines: 164- This looks a little different to what we're used to seeing. There are no calls to :meth:`~Sphinx.add_directive` or even :meth:`~Sphinx.add_role`. Instead, we @@ -192,8 +185,8 @@ You can now use the extension throughout your project. For example: Joe's Recipes ============= - Below are a collection of my favourite receipes. I highly recommend the - :rcp:reref:`TomatoSoup` receipe in particular! + Below are a collection of my favourite recipes. I highly recommend the + :recipe:ref:`TomatoSoup` recipe in particular! .. toctree:: @@ -204,15 +197,15 @@ You can now use the extension throughout your project. For example: The recipe contains `tomato` and `cilantro`. - .. rcp:recipe:: TomatoSoup + .. recipe:recipe:: TomatoSoup :contains: tomato cilantro salt pepper This recipe is a tasty tomato soup, combine all ingredients and cook. -The important things to note are the use of the ``:rcp:recipe:`` role to +The important things to note are the use of the ``:recipe:ref:`` role to cross-reference the recipe actually defined elsewhere (using the -``:rcp:recipe:`` directive. +``:recipe:recipe:`` directive. Further reading diff --git a/doc/development/tutorials/todo.rst b/doc/development/tutorials/todo.rst index 8071bda68..c04e14e99 100644 --- a/doc/development/tutorials/todo.rst +++ b/doc/development/tutorials/todo.rst @@ -174,10 +174,10 @@ The node structure that the directive returns looks like this:: .. rubric:: The event handlers -Event handlers are one of Sphinx's most powerful features, providing a way to do -hook into any part of the documentation process. There are many hooks available, -as detailed in :doc:`/extdev/appapi`, and we're going to use a subset of them -here. +Event handlers are one of Sphinx's most powerful features, providing a way to +do hook into any part of the documentation process. There are many events +provided by Sphinx itself, as detailed in :ref:`the API guide <events>`, and +we're going to use a subset of them here. Let's look at the event handlers used in the above example. First, the one for the :event:`env-purge-doc` event: @@ -203,18 +203,19 @@ The other handler belongs to the :event:`doctree-resolved` event: :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 -written for this event is a bit more involved. If the ``todo_include_todos`` -config value (which we'll describe shortly) is false, all ``todo`` and -``todolist`` nodes are removed from the documents. If not, ``todo`` nodes just -stay where and how they are. ``todolist`` nodes are replaced by a list of todo -entries, complete with backlinks to the location where they come from. The list -items are composed of the nodes from the ``todo`` entry and docutils nodes -created on the fly: a paragraph for each entry, containing text that gives the -location, and a link (reference node containing an italic node) with the -backreference. The reference URI is built by ``app.builder.get_relative_uri`` -which creates a suitable URI depending on the used builder, and appending the -todo node's (the target's) ID as the anchor name. +(resolving) <build-phases>` and allows custom resolving to be done. The handler +we have written for this event is a bit more involved. If the +``todo_include_todos`` config value (which we'll describe shortly) is false, +all ``todo`` and ``todolist`` nodes are removed from the documents. If not, +``todo`` nodes just stay where and how they are. ``todolist`` nodes are +replaced by a list of todo entries, complete with backlinks to the location +where they come from. The list items are composed of the nodes from the +``todo`` entry and docutils nodes created on the fly: a paragraph for each +entry, containing text that gives the location, and a link (reference node +containing an italic node) with the backreference. The reference URI is built +by :meth:`sphinx.builders.Builder.get_relative_uri`` which creates a suitable +URI depending on the used builder, and appending the todo node's (the target's) +ID as the anchor name. .. rubric:: The ``setup`` function @@ -238,13 +239,13 @@ What the individual calls do is the following: If the third argument was ``'html'``, HTML documents would be full rebuild if the config value changed its value. This is needed for config values that - influence reading (build :ref:`phase 1 <build-phases>`). + influence reading (build :ref:`phase 1 (reading) <build-phases>`). * :meth:`~Sphinx.add_node` adds a new *node class* to the build system. It also can specify visitor functions for each supported output format. These visitor - functions are needed when the new nodes stay until :ref:`phase 4 <build-phases>` - -- since the ``todolist`` node is always replaced in :ref:`phase 3 <build-phases>`, - it doesn't need any. + functions are needed when the new nodes stay until :ref:`phase 4 (writing) + <build-phases>`. Since the ``todolist`` node is always replaced in + :ref:`phase 3 (resolving) <build-phases>`, it doesn't need any. * :meth:`~Sphinx.add_directive` adds a new *directive*, given by name and class. @@ -279,7 +280,7 @@ For example: sys.path.append(os.path.abspath("./_ext")) - extensions = ['helloworld'] + extensions = ['todo'] todo_include_todos = False |