Coverage for pyquickhelper/sphinxext/sphinx_quote_extension.py: 93%

182 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-03 02:21 +0200

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Defines a :epkg:`sphinx` extension for a quote. 

5""" 

6from docutils import nodes 

7from docutils.parsers.rst import directives 

8 

9import sphinx 

10from sphinx.locale import _ 

11from docutils.parsers.rst.directives.admonitions import BaseAdmonition 

12from docutils.statemachine import StringList 

13from sphinx.util.nodes import nested_parse_with_titles 

14from ..texthelper.texts_language import TITLES 

15 

16 

17class quote_node(nodes.admonition): 

18 """ 

19 Defines ``quote`` node. 

20 """ 

21 pass 

22 

23 

24class QuoteNode(BaseAdmonition): 

25 """ 

26 A ``quotedef`` entry, displayed in the form of an admonition. 

27 It takes the following options: 

28 

29 * *author* 

30 * *book* or *manga* or *film* or *show* or *disc* or 

31 *comic* or *child* or *ado* 

32 * *year* 

33 * *pages* 

34 * *tag* 

35 * *source* 

36 * *lid* or *label* 

37 * *index*, additional index words beside the title and the author 

38 * *date*, if the text was written or declared at specific date 

39 * *title1*, by default, the author comes first, if True, the title is 

40 

41 Example:: 

42 

43 .. quote:: 

44 :author: author 

45 :book: book 

46 :year: year 

47 :pages: pages (optional) 

48 :tag: something 

49 :lid: id (used for further reference) 

50 :source: optional 

51 :index: word 

52 

53 A monkey could... 

54 """ 

55 

56 node_class = quote_node 

57 has_content = True 

58 required_arguments = 0 

59 optional_arguments = 0 

60 final_argument_whitespace = False 

61 option_spec = { 

62 'author': directives.unchanged, 

63 'book': directives.unchanged, 

64 'manga': directives.unchanged, 

65 'disc': directives.unchanged, 

66 'ado': directives.unchanged, 

67 'child': directives.unchanged, 

68 'comic': directives.unchanged, 

69 'show': directives.unchanged, 

70 'film': directives.unchanged, 

71 'year': directives.unchanged, 

72 'pages': directives.unchanged, 

73 'tag': directives.unchanged, 

74 'lid': directives.unchanged, 

75 'label': directives.unchanged, 

76 'source': directives.unchanged, 

77 'class': directives.class_option, 

78 'index': directives.unchanged, 

79 'date': directives.unchanged, 

80 'title1': directives.unchanged, 

81 } 

82 

83 def run(self): 

84 """ 

85 Builds the mathdef text. 

86 """ 

87 env = self.state.document.settings.env if hasattr( 

88 self.state.document.settings, "env") else None 

89 docname = None if env is None else env.docname 

90 if docname is not None: 

91 docname = docname.replace("\\", "/").split("/")[-1] 

92 language_code = self.state.document.settings.language_code if hasattr( 

93 self.state.document.settings, "language_code") else "en" 

94 

95 if not self.options.get('class'): 

96 self.options['class'] = ['admonition-quote'] 

97 

98 # body 

99 (quote,) = super(QuoteNode, self).run() 

100 if isinstance(quote, nodes.system_message): 

101 return [quote] # pragma: no cover 

102 

103 # mid 

104 tag = self.options.get('tag', 'quotetag').strip() 

105 if len(tag) == 0: 

106 raise ValueError("tag is empty") # pragma: no cover 

107 

108 def __(text): 

109 if text: 

110 return _(text) 

111 return "" 

112 

113 # book 

114 author = __(self.options.get('author', "").strip()) 

115 book = __(self.options.get('book', "").strip()) 

116 manga = __(self.options.get('manga', "").strip()) 

117 comic = __(self.options.get('comic', "").strip()) 

118 ado = __(self.options.get('ado', "").strip()) 

119 child = __(self.options.get('child', "").strip()) 

120 disc = __(self.options.get('disc', "").strip()) 

121 film = __(self.options.get('film', "").strip()) 

122 show = __(self.options.get('show', "").strip()) 

123 pages = __(self.options.get('pages', "").strip()) 

124 year = __(self.options.get('year', "").strip()) 

125 source = __(self.options.get('source', "").strip()) 

126 index = __(self.options.get('index', "").strip()) 

127 date = __(self.options.get('date', "").strip()) 

128 title1 = __(self.options.get('title1', "").strip() 

129 ) in ('1', 1, 'True', True, 'true') 

130 

131 indexes = [] 

132 if index: 

133 indexes.append(index) # pragma: no cover 

134 

135 # add a label 

136 lid = self.options.get('lid', self.options.get('label', None)) 

137 if lid: 

138 tnl = ['', f".. _{lid}:", ""] 

139 else: 

140 tnl = [] # pragma: no cover 

141 

142 if title1: 

143 if ado: 

144 tnl.append(f"**{ado}**") 

145 if child: 

146 tnl.append(f"**{child}**") 

147 if comic: 

148 tnl.append(f"**{comic}**") 

149 if disc: 

150 tnl.append(f"**{disc}**") 

151 if book: 

152 tnl.append(f"**{book}**") 

153 if manga: 

154 tnl.append(f"**{manga}**") 

155 if show: 

156 tnl.append(f"**{show}**") 

157 if film: 

158 tnl.append(f"**{film}**") 

159 if author: 

160 tnl.append(f"*{author}*, ") 

161 else: 

162 if author: 

163 tnl.append(f"**{author}**, ") 

164 if ado: 

165 tnl.append(f"*{ado}*") 

166 if child: 

167 tnl.append(f"*{child}*") 

168 if comic: 

169 tnl.append(f"*{comic}*") 

170 if disc: 

171 tnl.append(f"*{disc}*") 

172 if book: 

173 tnl.append(f"*{book}*") 

174 if manga: 

175 tnl.append(f"*{manga}*") 

176 if show: 

177 tnl.append(f"*{show}*") 

178 if film: 

179 tnl.append(f"*{film}*") 

180 

181 if author: 

182 indexes.append(author) 

183 indexes.append(TITLES[language_code]['author'] + "; " + author) 

184 if ado: 

185 indexes.append(ado) 

186 indexes.append(TITLES[language_code]['ado'] + "; " + ado) 

187 if child: 

188 indexes.append(child) 

189 indexes.append(TITLES[language_code]['child'] + "; " + child) 

190 if comic: 

191 indexes.append(comic) 

192 indexes.append(TITLES[language_code]['comic'] + "; " + comic) 

193 if disc: 

194 indexes.append(disc) 

195 indexes.append(TITLES[language_code]['disc'] + "; " + disc) 

196 if book: 

197 indexes.append(book) 

198 indexes.append(TITLES[language_code]['book'] + "; " + book) 

199 if manga: 

200 indexes.append(manga) 

201 indexes.append(TITLES[language_code]['manga'] + "; " + manga) 

202 if show: 

203 indexes.append(show) 

204 indexes.append(TITLES[language_code]['show'] + "; " + show) 

205 if film: 

206 indexes.append(film) 

207 indexes.append(TITLES[language_code]['film'] + "; " + film) 

208 

209 if pages: 

210 tnl.append(f", {pages}") 

211 if date: 

212 tnl.append(f" ({date})") 

213 if year: 

214 tnl.append(f" ({year})") 

215 if source: 

216 if source.startswith("http"): 

217 tnl.append(f", `source <{source}>`_") 

218 else: 

219 tnl.append(f", {source}") 

220 tnl.append('') 

221 tnl.append(".. index:: " + ", ".join(indexes)) 

222 tnl.append('') 

223 

224 content = StringList(tnl) 

225 content = content + self.content 

226 node = quote_node() 

227 

228 try: 

229 nested_parse_with_titles(self.state, content, node) 

230 except Exception as e: # pragma: no cover 

231 from sphinx.util import logging 

232 logger = logging.getLogger("blogpost") 

233 logger.warning( 

234 "[blogpost] unable to parse %r - %r - %r", author, book or manga, e) 

235 raise e 

236 

237 node['tag'] = tag 

238 node['author'] = author 

239 node['pages'] = pages 

240 node['year'] = year 

241 node['label'] = lid 

242 node['source'] = source 

243 node['book'] = book 

244 node['manga'] = manga 

245 node['disc'] = disc 

246 node['comic'] = comic 

247 node['ado'] = ado 

248 node['child'] = child 

249 node['film'] = film 

250 node['show'] = show 

251 node['index'] = index 

252 node['content'] = '\n'.join(self.content) 

253 node['classes'] += ["quote"] 

254 

255 return [node] 

256 

257 

258def visit_quote_node(self, node): 

259 """ 

260 visit_quote_node 

261 """ 

262 self.visit_admonition(node) 

263 

264 

265def depart_quote_node(self, node): 

266 """ 

267 depart_quote_node, 

268 see https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/html.py 

269 """ 

270 self.depart_admonition(node) 

271 

272 

273def visit_quote_node_rst(self, node): 

274 """ 

275 visit_quote_node 

276 """ 

277 self.new_state(0) 

278 self.add_text(".. quote::") 

279 for k, v in sorted(node.attributes.items()): 

280 if k in ("content", 'classes'): 

281 continue 

282 if v: 

283 self.new_state(4) 

284 self.add_text(f":{k}: {v}") 

285 self.end_state(wrap=False, end=None) 

286 self.add_text(self.nl) 

287 self.new_state(4) 

288 self.add_text(node['content']) 

289 self.end_state() 

290 self.end_state() 

291 raise nodes.SkipNode 

292 

293 

294def depart_quote_node_rst(self, node): 

295 """ 

296 depart_quote_node, 

297 see https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/html.py 

298 """ 

299 pass 

300 

301 

302def setup(app): 

303 """ 

304 setup for ``mathdef`` (sphinx) 

305 """ 

306 if hasattr(app, "add_mapping"): 

307 app.add_mapping('quote', quote_node) 

308 

309 app.add_node(quote_node, 

310 html=(visit_quote_node, depart_quote_node), 

311 epub=(visit_quote_node, depart_quote_node), 

312 elatex=(visit_quote_node, depart_quote_node), 

313 latex=(visit_quote_node, depart_quote_node), 

314 text=(visit_quote_node, depart_quote_node), 

315 md=(visit_quote_node, depart_quote_node), 

316 rst=(visit_quote_node_rst, depart_quote_node_rst)) 

317 

318 app.add_directive('quote', QuoteNode) 

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