Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Defines a sphinx extension ``tpl``, a role which use templating.
5"""
7import sphinx
8from docutils import nodes
9from ..texthelper import apply_template
12class tpl_node(nodes.TextElement):
14 """
15 Defines *tpl* node.
16 """
17 pass
20class ClassStruct:
21 """
22 Class as struct.
23 """
25 def __init__(self, **kwargs):
26 """
27 All arguments are added to the class.
28 """
29 for k, v in kwargs.items():
30 setattr(self, k, v)
33def evaluate_template(template, engine="jinja2", **kwargs):
34 """
35 Evaluate a template given a list of parameters given
36 a list of named parameters.
38 @param template template (:epkg:`jinja2`)
39 @param engine :epkg:`jinja2` or :epkg:`mako`
40 @param kwargs additional parameters
41 @return outcome
43 The function uses @see fn apply_template.
44 """
45 return apply_template(template, context=kwargs, engine=engine)
48def tpl_role(role, rawtext, text, lineno, inliner, options=None, content=None):
49 """
50 Defines custom role *tpl*. A template must be specified in
51 the configuration file.
53 ::
55 :tpl:`template_name,p1=v2, p2=v2, ...`
57 The role evaluate this expression with function :epkg:`*pyf:eval`:
59 ::
61 evaluate_template(template, p1=v1, p2=v2, ...)
63 You can switch engine by adding parameter ``engine='mako'``.
64 In the configuration file, the following must be added:
66 ::
68 tpl_template = {'template_name': 'some template'}
70 ``template_name`` can also be a function.
72 ::
74 tpl_template = {'py': python_link_doc}
76 And the corresponding line in the documentation:
78 ::
80 :tpl:`py,m='ftplib',o='FTP.storbinary'`
82 Which gives: :tpl:`py,m='ftplib',o='FTP.storbinary'` based on
83 function :func:`python_link_doc <pyquickhelper.sphinxext.documentation_link.python_link_doc>`.
85 :param role: The role name used in the document.
86 :param rawtext: The entire markup snippet, with role.
87 :param text: The text marked with the role.
88 :param lineno: The line number where rawtext appears in the input.
89 :param inliner: The inliner instance that called us.
90 :param options: Directive options for customization.
91 :param content: The directive content for customization.
92 """
93 spl = text.split(",")
94 template_name = spl[0]
95 if len(spl) == 1:
96 context = "" # pragma: no cover
97 else:
98 context = ",".join(spl[1:])
100 env = inliner.document.settings.env
101 app = env.app
102 config = app.config
104 try:
105 tpl_template = config.tpl_template
106 except AttributeError as e: # pragma: no cover
107 ma = "\n".join(sorted(str(_) for _ in app.config))
108 raise AttributeError(
109 "unable to find 'tpl_template' in configuration. Available:\n{0}".format(ma)) from e
111 if template_name not in tpl_template:
112 keys = "\n".join(sorted(tpl_template)) # pragma: no cover
113 raise ValueError( # pragma: no cover
114 "Unable to find template '{0}' in tpl_template. Found:\n{1}".format(template_name, keys))
115 tpl_content = tpl_template[template_name]
117 code = "dict(" + context + ")"
118 try:
119 val_context = eval(code)
120 except Exception as e: # pragma: no cover
121 raise Exception( # pragma: no cover
122 "Unable to compile '''{0}'''".format(code)) from e
124 if isinstance(tpl_content, str):
125 res = evaluate_template(tpl_content, **val_context)
126 else:
127 res = tpl_content(**val_context)
129 node = tpl_node(rawtext=rawtext)
130 node['classes'] += ["tpl"]
132 memo = ClassStruct(document=inliner.document, reporter=inliner.reporter,
133 language=inliner.language)
134 processed, messages = inliner.parse(res, lineno, memo, node)
135 if len(messages) > 0:
136 msg = inliner.reporter.error( # pragma: no cover
137 "unable to interpret '{0}', messages={1}".format(
138 text, ", ".join(str(_) for _ in messages)), line=lineno)
139 prb = inliner.problematic(rawtext, rawtext, msg) # pragma: no cover
140 return [prb], [msg] # pragma: no cover
142 node += processed
143 return [node], []
146def visit_tpl_node(self, node):
147 """
148 What to do when visiting a node *tpl*.
149 """
150 pass
153def depart_tpl_node(self, node):
154 """
155 What to do when leaving a node *tpl*.
156 """
157 pass
160def setup(app):
161 """
162 setup for ``bigger`` (sphinx)
163 """
164 if hasattr(app, "add_mapping"):
165 app.add_mapping('tpl', tpl_node)
167 app.add_config_value('tpl_template', {}, 'env')
168 app.add_node(tpl_node,
169 html=(visit_tpl_node, depart_tpl_node),
170 epub=(visit_tpl_node, depart_tpl_node),
171 elatex=(visit_tpl_node, depart_tpl_node),
172 latex=(visit_tpl_node, depart_tpl_node),
173 rst=(visit_tpl_node, depart_tpl_node),
174 md=(visit_tpl_node, depart_tpl_node),
175 text=(visit_tpl_node, depart_tpl_node))
177 app.add_role('tpl', tpl_role)
178 return {'version': sphinx.__display_version__, 'parallel_read_safe': True}