Hide keyboard shortcuts

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""" 

6 

7import sphinx 

8from docutils import nodes 

9from ..texthelper import apply_template 

10 

11 

12class tpl_node(nodes.TextElement): 

13 

14 """ 

15 Defines *tpl* node. 

16 """ 

17 pass 

18 

19 

20class ClassStruct: 

21 """ 

22 Class as struct. 

23 """ 

24 

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) 

31 

32 

33def evaluate_template(template, engine="jinja2", **kwargs): 

34 """ 

35 Evaluate a template given a list of parameters given 

36 a list of named parameters. 

37 

38 @param template template (:epkg:`jinja2`) 

39 @param engine :epkg:`jinja2` or :epkg:`mako` 

40 @param kwargs additional parameters 

41 @return outcome 

42 

43 The function uses @see fn apply_template. 

44 """ 

45 return apply_template(template, context=kwargs, engine=engine) 

46 

47 

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. 

52 

53 :: 

54 

55 :tpl:`template_name,p1=v2, p2=v2, ...` 

56 

57 The role evaluate this expression with function :epkg:`*pyf:eval`: 

58 

59 :: 

60 

61 evaluate_template(template, p1=v1, p2=v2, ...) 

62 

63 You can switch engine by adding parameter ``engine='mako'``. 

64 In the configuration file, the following must be added: 

65 

66 :: 

67 

68 tpl_template = {'template_name': 'some template'} 

69 

70 ``template_name`` can also be a function. 

71 

72 :: 

73 

74 tpl_template = {'py': python_link_doc} 

75 

76 And the corresponding line in the documentation: 

77 

78 :: 

79 

80 :tpl:`py,m='ftplib',o='FTP.storbinary'` 

81 

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>`. 

84 

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:]) 

99 

100 env = inliner.document.settings.env 

101 app = env.app 

102 config = app.config 

103 

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 

110 

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] 

116 

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 

123 

124 if isinstance(tpl_content, str): 

125 res = evaluate_template(tpl_content, **val_context) 

126 else: 

127 res = tpl_content(**val_context) 

128 

129 node = tpl_node(rawtext=rawtext) 

130 node['classes'] += ["tpl"] 

131 

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 

141 

142 node += processed 

143 return [node], [] 

144 

145 

146def visit_tpl_node(self, node): 

147 """ 

148 What to do when visiting a node *tpl*. 

149 """ 

150 pass 

151 

152 

153def depart_tpl_node(self, node): 

154 """ 

155 What to do when leaving a node *tpl*. 

156 """ 

157 pass 

158 

159 

160def setup(app): 

161 """ 

162 setup for ``bigger`` (sphinx) 

163 """ 

164 if hasattr(app, "add_mapping"): 

165 app.add_mapping('tpl', tpl_node) 

166 

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)) 

176 

177 app.add_role('tpl', tpl_role) 

178 return {'version': sphinx.__display_version__, 'parallel_read_safe': True}