comparison env/lib/python3.9/site-packages/docutils/parsers/rst/roles.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # $Id: roles.py 8347 2019-08-26 12:12:02Z milde $
2 # Author: Edward Loper <edloper@gradient.cis.upenn.edu>
3 # Copyright: This module has been placed in the public domain.
4
5 """
6 This module defines standard interpreted text role functions, a registry for
7 interpreted text roles, and an API for adding to and retrieving from the
8 registry.
9
10 The interface for interpreted role functions is as follows::
11
12 def role_fn(name, rawtext, text, lineno, inliner,
13 options={}, content=[]):
14 code...
15
16 # Set function attributes for customization:
17 role_fn.options = ...
18 role_fn.content = ...
19
20 Parameters:
21
22 - ``name`` is the local name of the interpreted text role, the role name
23 actually used in the document.
24
25 - ``rawtext`` is a string containing the entire interpreted text construct.
26 Return it as a ``problematic`` node linked to a system message if there is a
27 problem.
28
29 - ``text`` is the interpreted text content, with backslash escapes converted
30 to nulls (``\x00``).
31
32 - ``lineno`` is the line number where the interpreted text beings.
33
34 - ``inliner`` is the Inliner object that called the role function.
35 It defines the following useful attributes: ``reporter``,
36 ``problematic``, ``memo``, ``parent``, ``document``.
37
38 - ``options``: A dictionary of directive options for customization, to be
39 interpreted by the role function. Used for additional attributes for the
40 generated elements and other functionality.
41
42 - ``content``: A list of strings, the directive content for customization
43 ("role" directive). To be interpreted by the role function.
44
45 Function attributes for customization, interpreted by the "role" directive:
46
47 - ``options``: A dictionary, mapping known option names to conversion
48 functions such as `int` or `float`. ``None`` or an empty dict implies no
49 options to parse. Several directive option conversion functions are defined
50 in the `directives` module.
51
52 All role functions implicitly support the "class" option, unless disabled
53 with an explicit ``{'class': None}``.
54
55 - ``content``: A boolean; true if content is allowed. Client code must handle
56 the case where content is required but not supplied (an empty content list
57 will be supplied).
58
59 Note that unlike directives, the "arguments" function attribute is not
60 supported for role customization. Directive arguments are handled by the
61 "role" directive itself.
62
63 Interpreted role functions return a tuple of two values:
64
65 - A list of nodes which will be inserted into the document tree at the
66 point where the interpreted role was encountered (can be an empty
67 list).
68
69 - A list of system messages, which will be inserted into the document tree
70 immediately after the end of the current inline block (can also be empty).
71 """
72
73 __docformat__ = 'reStructuredText'
74
75 from docutils import nodes, utils
76 from docutils.parsers.rst import directives
77 from docutils.parsers.rst.languages import en as _fallback_language_module
78 from docutils.utils.code_analyzer import Lexer, LexerError
79
80 DEFAULT_INTERPRETED_ROLE = 'title-reference'
81 """
82 The canonical name of the default interpreted role. This role is used
83 when no role is specified for a piece of interpreted text.
84 """
85
86 _role_registry = {}
87 """Mapping of canonical role names to role functions. Language-dependent role
88 names are defined in the ``language`` subpackage."""
89
90 _roles = {}
91 """Mapping of local or language-dependent interpreted text role names to role
92 functions."""
93
94 def role(role_name, language_module, lineno, reporter):
95 """
96 Locate and return a role function from its language-dependent name, along
97 with a list of system messages. If the role is not found in the current
98 language, check English. Return a 2-tuple: role function (``None`` if the
99 named role cannot be found) and a list of system messages.
100 """
101 normname = role_name.lower()
102 messages = []
103 msg_text = []
104
105 if normname in _roles:
106 return _roles[normname], messages
107
108 if role_name:
109 canonicalname = None
110 try:
111 canonicalname = language_module.roles[normname]
112 except AttributeError as error:
113 msg_text.append('Problem retrieving role entry from language '
114 'module %r: %s.' % (language_module, error))
115 except KeyError:
116 msg_text.append('No role entry for "%s" in module "%s".'
117 % (role_name, language_module.__name__))
118 else:
119 canonicalname = DEFAULT_INTERPRETED_ROLE
120
121 # If we didn't find it, try English as a fallback.
122 if not canonicalname:
123 try:
124 canonicalname = _fallback_language_module.roles[normname]
125 msg_text.append('Using English fallback for role "%s".'
126 % role_name)
127 except KeyError:
128 msg_text.append('Trying "%s" as canonical role name.'
129 % role_name)
130 # The canonical name should be an English name, but just in case:
131 canonicalname = normname
132
133 # Collect any messages that we generated.
134 if msg_text:
135 message = reporter.info('\n'.join(msg_text), line=lineno)
136 messages.append(message)
137
138 # Look the role up in the registry, and return it.
139 if canonicalname in _role_registry:
140 role_fn = _role_registry[canonicalname]
141 register_local_role(normname, role_fn)
142 return role_fn, messages
143 else:
144 return None, messages # Error message will be generated by caller.
145
146 def register_canonical_role(name, role_fn):
147 """
148 Register an interpreted text role by its canonical name.
149
150 :Parameters:
151 - `name`: The canonical name of the interpreted role.
152 - `role_fn`: The role function. See the module docstring.
153 """
154 set_implicit_options(role_fn)
155 _role_registry[name] = role_fn
156
157 def register_local_role(name, role_fn):
158 """
159 Register an interpreted text role by its local or language-dependent name.
160
161 :Parameters:
162 - `name`: The local or language-dependent name of the interpreted role.
163 - `role_fn`: The role function. See the module docstring.
164 """
165 set_implicit_options(role_fn)
166 _roles[name] = role_fn
167
168 def set_implicit_options(role_fn):
169 """
170 Add customization options to role functions, unless explicitly set or
171 disabled.
172 """
173 if not hasattr(role_fn, 'options') or role_fn.options is None:
174 role_fn.options = {'class': directives.class_option}
175 elif 'class' not in role_fn.options:
176 role_fn.options['class'] = directives.class_option
177
178 def register_generic_role(canonical_name, node_class):
179 """For roles which simply wrap a given `node_class` around the text."""
180 role = GenericRole(canonical_name, node_class)
181 register_canonical_role(canonical_name, role)
182
183
184 class GenericRole(object):
185
186 """
187 Generic interpreted text role, where the interpreted text is simply
188 wrapped with the provided node class.
189 """
190
191 def __init__(self, role_name, node_class):
192 self.name = role_name
193 self.node_class = node_class
194
195 def __call__(self, role, rawtext, text, lineno, inliner,
196 options={}, content=[]):
197 set_classes(options)
198 return [self.node_class(rawtext, text, **options)], []
199
200
201 class CustomRole(object):
202
203 """
204 Wrapper for custom interpreted text roles.
205 """
206
207 def __init__(self, role_name, base_role, options={}, content=[]):
208 self.name = role_name
209 self.base_role = base_role
210 self.options = None
211 if hasattr(base_role, 'options'):
212 self.options = base_role.options
213 self.content = None
214 if hasattr(base_role, 'content'):
215 self.content = base_role.content
216 self.supplied_options = options
217 self.supplied_content = content
218
219 def __call__(self, role, rawtext, text, lineno, inliner,
220 options={}, content=[]):
221 opts = self.supplied_options.copy()
222 opts.update(options)
223 cont = list(self.supplied_content)
224 if cont and content:
225 cont += '\n'
226 cont.extend(content)
227 return self.base_role(role, rawtext, text, lineno, inliner,
228 options=opts, content=cont)
229
230
231 def generic_custom_role(role, rawtext, text, lineno, inliner,
232 options={}, content=[]):
233 """"""
234 # Once nested inline markup is implemented, this and other methods should
235 # recursively call inliner.nested_parse().
236 set_classes(options)
237 return [nodes.inline(rawtext, text, **options)], []
238
239 generic_custom_role.options = {'class': directives.class_option}
240
241
242 ######################################################################
243 # Define and register the standard roles:
244 ######################################################################
245
246 register_generic_role('abbreviation', nodes.abbreviation)
247 register_generic_role('acronym', nodes.acronym)
248 register_generic_role('emphasis', nodes.emphasis)
249 register_generic_role('literal', nodes.literal)
250 register_generic_role('strong', nodes.strong)
251 register_generic_role('subscript', nodes.subscript)
252 register_generic_role('superscript', nodes.superscript)
253 register_generic_role('title-reference', nodes.title_reference)
254
255 def pep_reference_role(role, rawtext, text, lineno, inliner,
256 options={}, content=[]):
257 try:
258 pepnum = int(utils.unescape(text))
259 if pepnum < 0 or pepnum > 9999:
260 raise ValueError
261 except ValueError:
262 msg = inliner.reporter.error(
263 'PEP number must be a number from 0 to 9999; "%s" is invalid.'
264 % text, line=lineno)
265 prb = inliner.problematic(rawtext, rawtext, msg)
266 return [prb], [msg]
267 # Base URL mainly used by inliner.pep_reference; so this is correct:
268 ref = (inliner.document.settings.pep_base_url
269 + inliner.document.settings.pep_file_url_template % pepnum)
270 set_classes(options)
271 return [nodes.reference(rawtext, 'PEP ' + text, refuri=ref,
272 **options)], []
273
274 register_canonical_role('pep-reference', pep_reference_role)
275
276 def rfc_reference_role(role, rawtext, text, lineno, inliner,
277 options={}, content=[]):
278 try:
279 if "#" in text:
280 rfcnum, section = utils.unescape(text).split("#", 1)
281 else:
282 rfcnum, section = utils.unescape(text), None
283 rfcnum = int(rfcnum)
284 if rfcnum < 1:
285 raise ValueError
286 except ValueError:
287 msg = inliner.reporter.error(
288 'RFC number must be a number greater than or equal to 1; '
289 '"%s" is invalid.' % text, line=lineno)
290 prb = inliner.problematic(rawtext, rawtext, msg)
291 return [prb], [msg]
292 # Base URL mainly used by inliner.rfc_reference, so this is correct:
293 ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
294 if section is not None:
295 ref += "#"+section
296 set_classes(options)
297 node = nodes.reference(rawtext, 'RFC ' + str(rfcnum), refuri=ref,
298 **options)
299 return [node], []
300
301 register_canonical_role('rfc-reference', rfc_reference_role)
302
303 def raw_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
304 if not inliner.document.settings.raw_enabled:
305 msg = inliner.reporter.warning('raw (and derived) roles disabled')
306 prb = inliner.problematic(rawtext, rawtext, msg)
307 return [prb], [msg]
308 if 'format' not in options:
309 msg = inliner.reporter.error(
310 'No format (Writer name) is associated with this role: "%s".\n'
311 'The "raw" role cannot be used directly.\n'
312 'Instead, use the "role" directive to create a new role with '
313 'an associated format.' % role, line=lineno)
314 prb = inliner.problematic(rawtext, rawtext, msg)
315 return [prb], [msg]
316 set_classes(options)
317 node = nodes.raw(rawtext, utils.unescape(text, True), **options)
318 node.source, node.line = inliner.reporter.get_source_and_line(lineno)
319 return [node], []
320
321 raw_role.options = {'format': directives.unchanged}
322
323 register_canonical_role('raw', raw_role)
324
325 def code_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
326 set_classes(options)
327 language = options.get('language', '')
328 classes = ['code']
329 if 'classes' in options:
330 classes.extend(options['classes'])
331 if language and language not in classes:
332 classes.append(language)
333 try:
334 tokens = Lexer(utils.unescape(text, True), language,
335 inliner.document.settings.syntax_highlight)
336 except LexerError as error:
337 msg = inliner.reporter.warning(error)
338 prb = inliner.problematic(rawtext, rawtext, msg)
339 return [prb], [msg]
340
341 node = nodes.literal(rawtext, '', classes=classes)
342
343 # analyse content and add nodes for every token
344 for classes, value in tokens:
345 if classes:
346 node += nodes.inline(value, value, classes=classes)
347 else:
348 # insert as Text to decrease the verbosity of the output
349 node += nodes.Text(value, value)
350
351 return [node], []
352
353 code_role.options = {'class': directives.class_option,
354 'language': directives.unchanged}
355
356 register_canonical_role('code', code_role)
357
358 def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
359 set_classes(options)
360 i = rawtext.find('`')
361 text = rawtext.split('`')[1]
362 node = nodes.math(rawtext, text, **options)
363 return [node], []
364
365 register_canonical_role('math', math_role)
366
367 ######################################################################
368 # Register roles that are currently unimplemented.
369 ######################################################################
370
371 def unimplemented_role(role, rawtext, text, lineno, inliner, attributes={}):
372 msg = inliner.reporter.error(
373 'Interpreted text role "%s" not implemented.' % role, line=lineno)
374 prb = inliner.problematic(rawtext, rawtext, msg)
375 return [prb], [msg]
376
377 register_canonical_role('index', unimplemented_role)
378 register_canonical_role('named-reference', unimplemented_role)
379 register_canonical_role('anonymous-reference', unimplemented_role)
380 register_canonical_role('uri-reference', unimplemented_role)
381 register_canonical_role('footnote-reference', unimplemented_role)
382 register_canonical_role('citation-reference', unimplemented_role)
383 register_canonical_role('substitution-reference', unimplemented_role)
384 register_canonical_role('target', unimplemented_role)
385
386 # This should remain unimplemented, for testing purposes:
387 register_canonical_role('restructuredtext-unimplemented-role',
388 unimplemented_role)
389
390
391 def set_classes(options):
392 """
393 Auxiliary function to set options['classes'] and delete
394 options['class'].
395 """
396 if 'class' in options:
397 assert 'classes' not in options
398 options['classes'] = options['class']
399 del options['class']