Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/docutils/parsers/rst/directives/__init__.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: __init__.py 8367 2019-08-27 12:09:56Z milde $ | |
2 # Author: David Goodger <goodger@python.org> | |
3 # Copyright: This module has been placed in the public domain. | |
4 | |
5 """ | |
6 This package contains directive implementation modules. | |
7 """ | |
8 | |
9 __docformat__ = 'reStructuredText' | |
10 | |
11 import re | |
12 import codecs | |
13 import sys | |
14 | |
15 from docutils import nodes | |
16 from docutils.utils import split_escaped_whitespace, escape2null, unescape | |
17 from docutils.parsers.rst.languages import en as _fallback_language_module | |
18 | |
19 if sys.version_info >= (3, 0): | |
20 unichr = chr # noqa | |
21 | |
22 | |
23 _directive_registry = { | |
24 'attention': ('admonitions', 'Attention'), | |
25 'caution': ('admonitions', 'Caution'), | |
26 'code': ('body', 'CodeBlock'), | |
27 'danger': ('admonitions', 'Danger'), | |
28 'error': ('admonitions', 'Error'), | |
29 'important': ('admonitions', 'Important'), | |
30 'note': ('admonitions', 'Note'), | |
31 'tip': ('admonitions', 'Tip'), | |
32 'hint': ('admonitions', 'Hint'), | |
33 'warning': ('admonitions', 'Warning'), | |
34 'admonition': ('admonitions', 'Admonition'), | |
35 'sidebar': ('body', 'Sidebar'), | |
36 'topic': ('body', 'Topic'), | |
37 'line-block': ('body', 'LineBlock'), | |
38 'parsed-literal': ('body', 'ParsedLiteral'), | |
39 'math': ('body', 'MathBlock'), | |
40 'rubric': ('body', 'Rubric'), | |
41 'epigraph': ('body', 'Epigraph'), | |
42 'highlights': ('body', 'Highlights'), | |
43 'pull-quote': ('body', 'PullQuote'), | |
44 'compound': ('body', 'Compound'), | |
45 'container': ('body', 'Container'), | |
46 #'questions': ('body', 'question_list'), | |
47 'table': ('tables', 'RSTTable'), | |
48 'csv-table': ('tables', 'CSVTable'), | |
49 'list-table': ('tables', 'ListTable'), | |
50 'image': ('images', 'Image'), | |
51 'figure': ('images', 'Figure'), | |
52 'contents': ('parts', 'Contents'), | |
53 'sectnum': ('parts', 'Sectnum'), | |
54 'header': ('parts', 'Header'), | |
55 'footer': ('parts', 'Footer'), | |
56 #'footnotes': ('parts', 'footnotes'), | |
57 #'citations': ('parts', 'citations'), | |
58 'target-notes': ('references', 'TargetNotes'), | |
59 'meta': ('html', 'Meta'), | |
60 #'imagemap': ('html', 'imagemap'), | |
61 'raw': ('misc', 'Raw'), | |
62 'include': ('misc', 'Include'), | |
63 'replace': ('misc', 'Replace'), | |
64 'unicode': ('misc', 'Unicode'), | |
65 'class': ('misc', 'Class'), | |
66 'role': ('misc', 'Role'), | |
67 'default-role': ('misc', 'DefaultRole'), | |
68 'title': ('misc', 'Title'), | |
69 'date': ('misc', 'Date'), | |
70 'restructuredtext-test-directive': ('misc', 'TestDirective'),} | |
71 """Mapping of directive name to (module name, class name). The | |
72 directive name is canonical & must be lowercase. Language-dependent | |
73 names are defined in the ``language`` subpackage.""" | |
74 | |
75 _directives = {} | |
76 """Cache of imported directives.""" | |
77 | |
78 def directive(directive_name, language_module, document): | |
79 """ | |
80 Locate and return a directive function from its language-dependent name. | |
81 If not found in the current language, check English. Return None if the | |
82 named directive cannot be found. | |
83 """ | |
84 normname = directive_name.lower() | |
85 messages = [] | |
86 msg_text = [] | |
87 if normname in _directives: | |
88 return _directives[normname], messages | |
89 canonicalname = None | |
90 try: | |
91 canonicalname = language_module.directives[normname] | |
92 except AttributeError as error: | |
93 msg_text.append('Problem retrieving directive entry from language ' | |
94 'module %r: %s.' % (language_module, error)) | |
95 except KeyError: | |
96 msg_text.append('No directive entry for "%s" in module "%s".' | |
97 % (directive_name, language_module.__name__)) | |
98 if not canonicalname: | |
99 try: | |
100 canonicalname = _fallback_language_module.directives[normname] | |
101 msg_text.append('Using English fallback for directive "%s".' | |
102 % directive_name) | |
103 except KeyError: | |
104 msg_text.append('Trying "%s" as canonical directive name.' | |
105 % directive_name) | |
106 # The canonical name should be an English name, but just in case: | |
107 canonicalname = normname | |
108 if msg_text: | |
109 message = document.reporter.info( | |
110 '\n'.join(msg_text), line=document.current_line) | |
111 messages.append(message) | |
112 try: | |
113 modulename, classname = _directive_registry[canonicalname] | |
114 except KeyError: | |
115 # Error handling done by caller. | |
116 return None, messages | |
117 try: | |
118 module = __import__(modulename, globals(), locals(), level=1) | |
119 except ImportError as detail: | |
120 messages.append(document.reporter.error( | |
121 'Error importing directive module "%s" (directive "%s"):\n%s' | |
122 % (modulename, directive_name, detail), | |
123 line=document.current_line)) | |
124 return None, messages | |
125 try: | |
126 directive = getattr(module, classname) | |
127 _directives[normname] = directive | |
128 except AttributeError: | |
129 messages.append(document.reporter.error( | |
130 'No directive class "%s" in module "%s" (directive "%s").' | |
131 % (classname, modulename, directive_name), | |
132 line=document.current_line)) | |
133 return None, messages | |
134 return directive, messages | |
135 | |
136 def register_directive(name, directive): | |
137 """ | |
138 Register a nonstandard application-defined directive function. | |
139 Language lookups are not needed for such functions. | |
140 """ | |
141 _directives[name] = directive | |
142 | |
143 def flag(argument): | |
144 """ | |
145 Check for a valid flag option (no argument) and return ``None``. | |
146 (Directive option conversion function.) | |
147 | |
148 Raise ``ValueError`` if an argument is found. | |
149 """ | |
150 if argument and argument.strip(): | |
151 raise ValueError('no argument is allowed; "%s" supplied' % argument) | |
152 else: | |
153 return None | |
154 | |
155 def unchanged_required(argument): | |
156 """ | |
157 Return the argument text, unchanged. | |
158 (Directive option conversion function.) | |
159 | |
160 Raise ``ValueError`` if no argument is found. | |
161 """ | |
162 if argument is None: | |
163 raise ValueError('argument required but none supplied') | |
164 else: | |
165 return argument # unchanged! | |
166 | |
167 def unchanged(argument): | |
168 """ | |
169 Return the argument text, unchanged. | |
170 (Directive option conversion function.) | |
171 | |
172 No argument implies empty string (""). | |
173 """ | |
174 if argument is None: | |
175 return u'' | |
176 else: | |
177 return argument # unchanged! | |
178 | |
179 def path(argument): | |
180 """ | |
181 Return the path argument unwrapped (with newlines removed). | |
182 (Directive option conversion function.) | |
183 | |
184 Raise ``ValueError`` if no argument is found. | |
185 """ | |
186 if argument is None: | |
187 raise ValueError('argument required but none supplied') | |
188 else: | |
189 path = ''.join([s.strip() for s in argument.splitlines()]) | |
190 return path | |
191 | |
192 def uri(argument): | |
193 """ | |
194 Return the URI argument with unescaped whitespace removed. | |
195 (Directive option conversion function.) | |
196 | |
197 Raise ``ValueError`` if no argument is found. | |
198 """ | |
199 if argument is None: | |
200 raise ValueError('argument required but none supplied') | |
201 else: | |
202 parts = split_escaped_whitespace(escape2null(argument)) | |
203 uri = ' '.join(''.join(unescape(part).split()) for part in parts) | |
204 return uri | |
205 | |
206 def nonnegative_int(argument): | |
207 """ | |
208 Check for a nonnegative integer argument; raise ``ValueError`` if not. | |
209 (Directive option conversion function.) | |
210 """ | |
211 value = int(argument) | |
212 if value < 0: | |
213 raise ValueError('negative value; must be positive or zero') | |
214 return value | |
215 | |
216 def percentage(argument): | |
217 """ | |
218 Check for an integer percentage value with optional percent sign. | |
219 """ | |
220 try: | |
221 argument = argument.rstrip(' %') | |
222 except AttributeError: | |
223 pass | |
224 return nonnegative_int(argument) | |
225 | |
226 length_units = ['em', 'ex', 'px', 'in', 'cm', 'mm', 'pt', 'pc'] | |
227 | |
228 def get_measure(argument, units): | |
229 """ | |
230 Check for a positive argument of one of the units and return a | |
231 normalized string of the form "<value><unit>" (without space in | |
232 between). | |
233 | |
234 To be called from directive option conversion functions. | |
235 """ | |
236 match = re.match(r'^([0-9.]+) *(%s)$' % '|'.join(units), argument) | |
237 try: | |
238 float(match.group(1)) | |
239 except (AttributeError, ValueError): | |
240 raise ValueError( | |
241 'not a positive measure of one of the following units:\n%s' | |
242 % ' '.join(['"%s"' % i for i in units])) | |
243 return match.group(1) + match.group(2) | |
244 | |
245 def length_or_unitless(argument): | |
246 return get_measure(argument, length_units + ['']) | |
247 | |
248 def length_or_percentage_or_unitless(argument, default=''): | |
249 """ | |
250 Return normalized string of a length or percentage unit. | |
251 | |
252 Add <default> if there is no unit. Raise ValueError if the argument is not | |
253 a positive measure of one of the valid CSS units (or without unit). | |
254 | |
255 >>> length_or_percentage_or_unitless('3 pt') | |
256 '3pt' | |
257 >>> length_or_percentage_or_unitless('3%', 'em') | |
258 '3%' | |
259 >>> length_or_percentage_or_unitless('3') | |
260 '3' | |
261 >>> length_or_percentage_or_unitless('3', 'px') | |
262 '3px' | |
263 """ | |
264 try: | |
265 return get_measure(argument, length_units + ['%']) | |
266 except ValueError: | |
267 try: | |
268 return get_measure(argument, ['']) + default | |
269 except ValueError: | |
270 # raise ValueError with list of valid units: | |
271 return get_measure(argument, length_units + ['%']) | |
272 | |
273 def class_option(argument): | |
274 """ | |
275 Convert the argument into a list of ID-compatible strings and return it. | |
276 (Directive option conversion function.) | |
277 | |
278 Raise ``ValueError`` if no argument is found. | |
279 """ | |
280 if argument is None: | |
281 raise ValueError('argument required but none supplied') | |
282 names = argument.split() | |
283 class_names = [] | |
284 for name in names: | |
285 class_name = nodes.make_id(name) | |
286 if not class_name: | |
287 raise ValueError('cannot make "%s" into a class name' % name) | |
288 class_names.append(class_name) | |
289 return class_names | |
290 | |
291 unicode_pattern = re.compile( | |
292 r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE) | |
293 | |
294 def unicode_code(code): | |
295 r""" | |
296 Convert a Unicode character code to a Unicode character. | |
297 (Directive option conversion function.) | |
298 | |
299 Codes may be decimal numbers, hexadecimal numbers (prefixed by ``0x``, | |
300 ``x``, ``\x``, ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style | |
301 numeric character entities (e.g. ``☮``). Other text remains as-is. | |
302 | |
303 Raise ValueError for illegal Unicode code values. | |
304 """ | |
305 try: | |
306 if code.isdigit(): # decimal number | |
307 return unichr(int(code)) | |
308 else: | |
309 match = unicode_pattern.match(code) | |
310 if match: # hex number | |
311 value = match.group(1) or match.group(2) | |
312 return unichr(int(value, 16)) | |
313 else: # other text | |
314 return code | |
315 except OverflowError as detail: | |
316 raise ValueError('code too large (%s)' % detail) | |
317 | |
318 def single_char_or_unicode(argument): | |
319 """ | |
320 A single character is returned as-is. Unicode characters codes are | |
321 converted as in `unicode_code`. (Directive option conversion function.) | |
322 """ | |
323 char = unicode_code(argument) | |
324 if len(char) > 1: | |
325 raise ValueError('%r invalid; must be a single character or ' | |
326 'a Unicode code' % char) | |
327 return char | |
328 | |
329 def single_char_or_whitespace_or_unicode(argument): | |
330 """ | |
331 As with `single_char_or_unicode`, but "tab" and "space" are also supported. | |
332 (Directive option conversion function.) | |
333 """ | |
334 if argument == 'tab': | |
335 char = '\t' | |
336 elif argument == 'space': | |
337 char = ' ' | |
338 else: | |
339 char = single_char_or_unicode(argument) | |
340 return char | |
341 | |
342 def positive_int(argument): | |
343 """ | |
344 Converts the argument into an integer. Raises ValueError for negative, | |
345 zero, or non-integer values. (Directive option conversion function.) | |
346 """ | |
347 value = int(argument) | |
348 if value < 1: | |
349 raise ValueError('negative or zero value; must be positive') | |
350 return value | |
351 | |
352 def positive_int_list(argument): | |
353 """ | |
354 Converts a space- or comma-separated list of values into a Python list | |
355 of integers. | |
356 (Directive option conversion function.) | |
357 | |
358 Raises ValueError for non-positive-integer values. | |
359 """ | |
360 if ',' in argument: | |
361 entries = argument.split(',') | |
362 else: | |
363 entries = argument.split() | |
364 return [positive_int(entry) for entry in entries] | |
365 | |
366 def encoding(argument): | |
367 """ | |
368 Verfies the encoding argument by lookup. | |
369 (Directive option conversion function.) | |
370 | |
371 Raises ValueError for unknown encodings. | |
372 """ | |
373 try: | |
374 codecs.lookup(argument) | |
375 except LookupError: | |
376 raise ValueError('unknown encoding: "%s"' % argument) | |
377 return argument | |
378 | |
379 def choice(argument, values): | |
380 """ | |
381 Directive option utility function, supplied to enable options whose | |
382 argument must be a member of a finite set of possible values (must be | |
383 lower case). A custom conversion function must be written to use it. For | |
384 example:: | |
385 | |
386 from docutils.parsers.rst import directives | |
387 | |
388 def yesno(argument): | |
389 return directives.choice(argument, ('yes', 'no')) | |
390 | |
391 Raise ``ValueError`` if no argument is found or if the argument's value is | |
392 not valid (not an entry in the supplied list). | |
393 """ | |
394 try: | |
395 value = argument.lower().strip() | |
396 except AttributeError: | |
397 raise ValueError('must supply an argument; choose from %s' | |
398 % format_values(values)) | |
399 if value in values: | |
400 return value | |
401 else: | |
402 raise ValueError('"%s" unknown; choose from %s' | |
403 % (argument, format_values(values))) | |
404 | |
405 def format_values(values): | |
406 return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]), | |
407 values[-1]) | |
408 | |
409 def value_or(values, other): | |
410 """ | |
411 The argument can be any of `values` or `argument_type`. | |
412 """ | |
413 def auto_or_other(argument): | |
414 if argument in values: | |
415 return argument | |
416 else: | |
417 return other(argument) | |
418 return auto_or_other | |
419 |