2 # Author: Edward Loper <edloper@gradient.cis.upenn.edu>
3 # Copyright: This module has been placed in the public domain.
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
10 The interface for interpreted role functions is as follows::
12 def role_fn(name, rawtext, text, lineno, inliner,
13 options={}, content=[]):
16 # Set function attributes for customization:
22 - ``name`` is the local name of the interpreted text role, the role name
23 actually used in the document.
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
29 - ``text`` is the interpreted text content, with backslash escapes converted
32 - ``lineno`` is the line number where the interpreted text beings.
34 - ``inliner`` is the Inliner object that called the role function.
35 It defines the following useful attributes: ``reporter``,
36 ``problematic``, ``memo``, ``parent``, ``document``.
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.
42 - ``content``: A list of strings, the directive content for customization
43 ("role" directive). To be interpreted by the role function.
45 Function attributes for customization, interpreted by the "role" directive:
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.
52 All role functions implicitly support the "class" option, unless disabled
53 with an explicit ``{'class': None}``.
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
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.
63 Interpreted role functions return a tuple of two values:
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
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).
73 __docformat__
= 'reStructuredText'
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
80 DEFAULT_INTERPRETED_ROLE
= 'title-reference'
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.
87 """Mapping of canonical role names to role functions. Language-dependent role
88 names are defined in the ``language`` subpackage."""
91 """Mapping of local or language-dependent interpreted text role names to role
94 def role(role_name
, language_module
, lineno
, reporter
):
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.
101 normname
= role_name
.lower()
105 if normname
in _roles
:
106 return _roles
[normname
], messages
111 canonicalname
= language_module
.roles
[normname
]
112 except AttributeError, error
:
113 msg_text
.append('Problem retrieving role entry from language '
114 'module %r: %s.' % (language_module
, error
))
116 msg_text
.append('No role entry for "%s" in module "%s".'
117 % (role_name
, language_module
.__name
__))
119 canonicalname
= DEFAULT_INTERPRETED_ROLE
121 # If we didn't find it, try English as a fallback.
122 if not canonicalname
:
124 canonicalname
= _fallback_language_module
.roles
[normname
]
125 msg_text
.append('Using English fallback for role "%s".'
128 msg_text
.append('Trying "%s" as canonical role name.'
130 # The canonical name should be an English name, but just in case:
131 canonicalname
= normname
133 # Collect any messages that we generated.
135 message
= reporter
.info('\n'.join(msg_text
), line
=lineno
)
136 messages
.append(message
)
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
144 return None, messages
# Error message will be generated by caller.
146 def register_canonical_role(name
, role_fn
):
148 Register an interpreted text role by its canonical name.
151 - `name`: The canonical name of the interpreted role.
152 - `role_fn`: The role function. See the module docstring.
154 set_implicit_options(role_fn
)
155 _role_registry
[name
] = role_fn
157 def register_local_role(name
, role_fn
):
159 Register an interpreted text role by its local or language-dependent name.
162 - `name`: The local or language-dependent name of the interpreted role.
163 - `role_fn`: The role function. See the module docstring.
165 set_implicit_options(role_fn
)
166 _roles
[name
] = role_fn
168 def set_implicit_options(role_fn
):
170 Add customization options to role functions, unless explicitly set or
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
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
)
187 Generic interpreted text role, where the interpreted text is simply
188 wrapped with the provided node class.
191 def __init__(self
, role_name
, node_class
):
192 self
.name
= role_name
193 self
.node_class
= node_class
195 def __call__(self
, role
, rawtext
, text
, lineno
, inliner
,
196 options
={}, content
=[]):
198 return [self
.node_class(rawtext
, utils
.unescape(text
), **options
)], []
204 Wrapper for custom interpreted text roles.
207 def __init__(self
, role_name
, base_role
, options
={}, content
=[]):
208 self
.name
= role_name
209 self
.base_role
= base_role
211 if hasattr(base_role
, 'options'):
212 self
.options
= base_role
.options
214 if hasattr(base_role
, 'content'):
215 self
.content
= base_role
.content
216 self
.supplied_options
= options
217 self
.supplied_content
= content
219 def __call__(self
, role
, rawtext
, text
, lineno
, inliner
,
220 options
={}, content
=[]):
221 opts
= self
.supplied_options
.copy()
223 cont
= list(self
.supplied_content
)
227 return self
.base_role(role
, rawtext
, text
, lineno
, inliner
,
228 options
=opts
, content
=cont
)
231 def generic_custom_role(role
, rawtext
, text
, lineno
, inliner
,
232 options
={}, content
=[]):
234 # Once nested inline markup is implemented, this and other methods should
235 # recursively call inliner.nested_parse().
237 return [nodes
.inline(rawtext
, utils
.unescape(text
), **options
)], []
239 generic_custom_role
.options
= {'class': directives
.class_option
}
242 ######################################################################
243 # Define and register the standard roles:
244 ######################################################################
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
)
255 def pep_reference_role(role
, rawtext
, text
, lineno
, inliner
,
256 options
={}, content
=[]):
259 if pepnum
< 0 or pepnum
> 9999:
262 msg
= inliner
.reporter
.error(
263 'PEP number must be a number from 0 to 9999; "%s" is invalid.'
265 prb
= inliner
.problematic(rawtext
, rawtext
, 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
)
271 return [nodes
.reference(rawtext
, 'PEP ' + utils
.unescape(text
), refuri
=ref
,
274 register_canonical_role('pep-reference', pep_reference_role
)
276 def rfc_reference_role(role
, rawtext
, text
, lineno
, inliner
,
277 options
={}, content
=[]):
283 msg
= inliner
.reporter
.error(
284 'RFC number must be a number greater than or equal to 1; '
285 '"%s" is invalid.' % text
, line
=lineno
)
286 prb
= inliner
.problematic(rawtext
, rawtext
, msg
)
288 # Base URL mainly used by inliner.rfc_reference, so this is correct:
289 ref
= inliner
.document
.settings
.rfc_base_url
+ inliner
.rfc_url
% rfcnum
291 node
= nodes
.reference(rawtext
, 'RFC ' + utils
.unescape(text
), refuri
=ref
,
295 register_canonical_role('rfc-reference', rfc_reference_role
)
297 def raw_role(role
, rawtext
, text
, lineno
, inliner
, options
={}, content
=[]):
298 if not inliner
.document
.settings
.raw_enabled
:
299 msg
= inliner
.reporter
.warning('raw (and derived) roles disabled')
300 prb
= inliner
.problematic(rawtext
, rawtext
, msg
)
302 if 'format' not in options
:
303 msg
= inliner
.reporter
.error(
304 'No format (Writer name) is associated with this role: "%s".\n'
305 'The "raw" role cannot be used directly.\n'
306 'Instead, use the "role" directive to create a new role with '
307 'an associated format.' % role
, line
=lineno
)
308 prb
= inliner
.problematic(rawtext
, rawtext
, msg
)
311 node
= nodes
.raw(rawtext
, utils
.unescape(text
, 1), **options
)
312 node
.source
, node
.line
= inliner
.reporter
.get_source_and_line(lineno
)
315 raw_role
.options
= {'format': directives
.unchanged
}
317 register_canonical_role('raw', raw_role
)
319 def code_role(role
, rawtext
, text
, lineno
, inliner
, options
={}, content
=[]):
321 language
= options
.get('language', '')
323 if 'classes' in options
:
324 classes
.extend(options
['classes'])
325 if language
and language
not in classes
:
326 classes
.append(language
)
328 tokens
= Lexer(utils
.unescape(text
, 1), language
,
329 inliner
.document
.settings
.syntax_highlight
)
330 except LexerError
, error
:
331 msg
= inliner
.reporter
.warning(error
)
332 prb
= inliner
.problematic(rawtext
, rawtext
, msg
)
335 node
= nodes
.literal(rawtext
, '', classes
=classes
)
337 # analyze content and add nodes for every token
338 for classes
, value
in tokens
:
339 # print (classes, value)
341 node
+= nodes
.inline(value
, value
, classes
=classes
)
343 # insert as Text to decrease the verbosity of the output
344 node
+= nodes
.Text(value
, value
)
348 code_role
.options
= {'class': directives
.class_option
,
349 'language': directives
.unchanged
}
351 register_canonical_role('code', code_role
)
353 def math_role(role
, rawtext
, text
, lineno
, inliner
, options
={}, content
=[]):
354 i
= rawtext
.find('`')
355 text
= rawtext
.split('`')[1]
356 node
= nodes
.math(rawtext
, text
)
359 register_canonical_role('math', math_role
)
361 ######################################################################
362 # Register roles that are currently unimplemented.
363 ######################################################################
365 def unimplemented_role(role
, rawtext
, text
, lineno
, inliner
, attributes
={}):
366 msg
= inliner
.reporter
.error(
367 'Interpreted text role "%s" not implemented.' % role
, line
=lineno
)
368 prb
= inliner
.problematic(rawtext
, rawtext
, msg
)
371 register_canonical_role('index', unimplemented_role
)
372 register_canonical_role('named-reference', unimplemented_role
)
373 register_canonical_role('anonymous-reference', unimplemented_role
)
374 register_canonical_role('uri-reference', unimplemented_role
)
375 register_canonical_role('footnote-reference', unimplemented_role
)
376 register_canonical_role('citation-reference', unimplemented_role
)
377 register_canonical_role('substitution-reference', unimplemented_role
)
378 register_canonical_role('target', unimplemented_role
)
380 # This should remain unimplemented, for testing purposes:
381 register_canonical_role('restructuredtext-unimplemented-role',
385 def set_classes(options
):
387 Auxiliary function to set options['classes'] and delete
390 if 'class' in options
:
391 assert 'classes' not in options
392 options
['classes'] = options
['class']