summaryrefslogtreecommitdiffstats
path: root/doc/development/tutorials/examples/recipe.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/tutorials/examples/recipe.py')
-rw-r--r--doc/development/tutorials/examples/recipe.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/doc/development/tutorials/examples/recipe.py b/doc/development/tutorials/examples/recipe.py
new file mode 100644
index 0000000..c7ebf2a
--- /dev/null
+++ b/doc/development/tutorials/examples/recipe.py
@@ -0,0 +1,159 @@
+from collections import defaultdict
+
+from docutils.parsers.rst import directives
+
+from sphinx import addnodes
+from sphinx.directives import ObjectDescription
+from sphinx.domains import Domain, Index
+from sphinx.roles import XRefRole
+from sphinx.util.nodes import make_refnode
+
+
+class RecipeDirective(ObjectDescription):
+ """A custom directive that describes a recipe."""
+
+ has_content = True
+ required_arguments = 1
+ option_spec = {
+ 'contains': directives.unchanged_required,
+ }
+
+ def handle_signature(self, sig, signode):
+ signode += addnodes.desc_name(text=sig)
+ return sig
+
+ def add_target_and_index(self, name_cls, sig, signode):
+ signode['ids'].append('recipe' + '-' + sig)
+ if 'contains' in self.options:
+ 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 index that creates an ingredient matrix."""
+
+ name = 'ingredient'
+ localname = 'Ingredient Index'
+ shortname = 'Ingredient'
+
+ def generate(self, docnames=None):
+ 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):
+ """A custom index that creates an recipe matrix."""
+
+ name = 'recipe'
+ localname = 'Recipe Index'
+ shortname = 'Recipe'
+
+ def generate(self, docnames=None):
+ 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, _priority 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 = 'recipe'
+ label = 'Recipe Sample'
+ roles = {
+ 'ref': XRefRole(),
+ }
+ directives = {
+ 'recipe': RecipeDirective,
+ }
+ indices = {
+ RecipeIndex,
+ IngredientIndex,
+ }
+ initial_data = {
+ 'recipes': [], # object list
+ 'recipe_ingredients': {}, # name -> object
+ }
+
+ def get_full_qualified_name(self, node):
+ return f'recipe.{node.arguments[0]}'
+
+ def get_objects(self):
+ yield from self.data['recipes']
+
+ 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]
+
+ if len(match) > 0:
+ todocname = match[0][0]
+ targ = match[0][1]
+
+ return make_refnode(builder, fromdocname, todocname, targ,
+ contnode, targ)
+ else:
+ print('Awww, found nothing')
+ return None
+
+ def add_recipe(self, signature, ingredients):
+ """Add a new recipe to the domain."""
+ name = f'recipe.{signature}'
+ anchor = f'recipe-{signature}'
+
+ self.data['recipe_ingredients'][name] = ingredients
+ # name, dispname, type, docname, anchor, priority
+ self.data['recipes'].append(
+ (name, signature, 'Recipe', self.env.docname, anchor, 0))
+
+
+def setup(app):
+ app.add_domain(RecipeDomain)
+
+ return {
+ 'version': '0.1',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }