Files
sphinx/doc/development/tutorials/examples/recipe.py
T

161 lines
5.0 KiB
Python
Raw Normal View History

2019-02-11 10:44:49 +00:00
from collections import defaultdict
2019-02-08 17:47:19 +00:00
from docutils.parsers.rst import directives
2019-02-25 11:02:24 +00:00
2019-02-08 17:47:19 +00:00
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain
from sphinx.domains import 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 = {
2019-02-11 10:44:49 +00:00
'contains': directives.unchanged_required,
2019-02-08 17:47:19 +00:00
}
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 'noindex' not in self.options:
2019-02-11 10:44:49 +00:00
ingredients = [
x.strip() for x in self.options.get('contains').split(',')]
recipes = self.env.get_domain('recipe')
recipes.add_recipe(sig, ingredients)
2019-02-08 17:47:19 +00:00
class IngredientIndex(Index):
2019-02-11 10:44:49 +00:00
"""A custom index that creates an ingredient matrix."""
2019-02-08 17:47:19 +00:00
2019-02-11 10:44:49 +00:00
name = 'ingredient'
2019-02-08 17:47:19 +00:00
localname = 'Ingredient Index'
shortname = 'Ingredient'
def generate(self, docnames=None):
2019-02-11 10:44:49 +00:00
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
2019-02-08 17:47:19 +00:00
class RecipeIndex(Index):
2019-02-11 10:44:49 +00:00
"""A custom index that creates an recipe matrix."""
name = 'recipe'
2019-02-08 17:47:19 +00:00
localname = 'Recipe Index'
shortname = 'Recipe'
def generate(self, docnames=None):
2019-02-11 10:44:49 +00:00
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
2019-02-08 17:47:19 +00:00
class RecipeDomain(Domain):
2019-02-11 10:44:49 +00:00
name = 'recipe'
2019-02-08 17:47:19 +00:00
label = 'Recipe Sample'
roles = {
2019-02-11 10:44:49 +00:00
'ref': XRefRole()
2019-02-08 17:47:19 +00:00
}
directives = {
'recipe': RecipeDirective,
}
indices = {
RecipeIndex,
IngredientIndex
}
initial_data = {
2019-02-11 10:44:49 +00:00
'recipes': [], # object list
'recipe_ingredients': {}, # name -> object
2019-02-08 17:47:19 +00:00
}
def get_full_qualified_name(self, node):
2019-02-11 10:44:49 +00:00
return '{}.{}'.format('recipe', node.arguments[0])
2019-02-08 17:47:19 +00:00
def get_objects(self):
2019-02-11 10:44:49 +00:00
for obj in self.data['recipes']:
2019-02-08 17:47:19 +00:00
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]
if len(match) > 0:
todocname = match[0][0]
targ = match[0][1]
return make_refnode(builder, fromdocname, todocname, targ,
contnode, targ)
else:
2019-02-11 10:44:49 +00:00
print('Awww, found nothing')
2019-02-08 17:47:19 +00:00
return None
2019-02-11 10:44:49 +00:00
def add_recipe(self, signature, ingredients):
"""Add a new recipe to the domain."""
name = '{}.{}'.format('recipe', signature)
anchor = 'recipe-{}'.format(signature)
2019-02-08 17:47:19 +00:00
2019-02-11 10:44:49 +00:00
self.data['recipe_ingredients'][name] = ingredients
# name, dispname, type, docname, anchor, priority
self.data['recipes'].append(
(name, signature, 'Recipe', self.env.docname, anchor, 0))
2019-02-08 17:47:19 +00:00
2019-02-11 10:44:49 +00:00
def setup(app):
app.add_domain(RecipeDomain)
2019-02-08 17:47:19 +00:00
2019-02-11 10:44:49 +00:00
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}