Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/docutils/transforms/universal.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
| author | shellac |
|---|---|
| date | Sat, 02 May 2020 07:14:21 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:26e78fe6e8c4 |
|---|---|
| 1 # $Id: universal.py 8393 2019-09-18 10:13:00Z milde $ | |
| 2 # -*- coding: utf-8 -*- | |
| 3 # Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer; Günter Milde | |
| 4 # Maintainer: docutils-develop@lists.sourceforge.net | |
| 5 # Copyright: This module has been placed in the public domain. | |
| 6 | |
| 7 """ | |
| 8 Transforms needed by most or all documents: | |
| 9 | |
| 10 - `Decorations`: Generate a document's header & footer. | |
| 11 - `Messages`: Placement of system messages stored in | |
| 12 `nodes.document.transform_messages`. | |
| 13 - `TestMessages`: Like `Messages`, used on test runs. | |
| 14 - `FinalReferences`: Resolve remaining references. | |
| 15 """ | |
| 16 | |
| 17 __docformat__ = 'reStructuredText' | |
| 18 | |
| 19 import re | |
| 20 import sys | |
| 21 import time | |
| 22 from docutils import nodes, utils | |
| 23 from docutils.transforms import TransformError, Transform | |
| 24 from docutils.utils import smartquotes | |
| 25 | |
| 26 | |
| 27 if sys.version_info >= (3, 0): | |
| 28 unicode = str # noqa | |
| 29 | |
| 30 | |
| 31 class Decorations(Transform): | |
| 32 | |
| 33 """ | |
| 34 Populate a document's decoration element (header, footer). | |
| 35 """ | |
| 36 | |
| 37 default_priority = 820 | |
| 38 | |
| 39 def apply(self): | |
| 40 header_nodes = self.generate_header() | |
| 41 if header_nodes: | |
| 42 decoration = self.document.get_decoration() | |
| 43 header = decoration.get_header() | |
| 44 header.extend(header_nodes) | |
| 45 footer_nodes = self.generate_footer() | |
| 46 if footer_nodes: | |
| 47 decoration = self.document.get_decoration() | |
| 48 footer = decoration.get_footer() | |
| 49 footer.extend(footer_nodes) | |
| 50 | |
| 51 def generate_header(self): | |
| 52 return None | |
| 53 | |
| 54 def generate_footer(self): | |
| 55 # @@@ Text is hard-coded for now. | |
| 56 # Should be made dynamic (language-dependent). | |
| 57 # @@@ Use timestamp from the `SOURCE_DATE_EPOCH`_ environment variable | |
| 58 # for the datestamp? | |
| 59 # See https://sourceforge.net/p/docutils/patches/132/ | |
| 60 # and https://reproducible-builds.org/specs/source-date-epoch/ | |
| 61 settings = self.document.settings | |
| 62 if settings.generator or settings.datestamp or settings.source_link \ | |
| 63 or settings.source_url: | |
| 64 text = [] | |
| 65 if settings.source_link and settings._source \ | |
| 66 or settings.source_url: | |
| 67 if settings.source_url: | |
| 68 source = settings.source_url | |
| 69 else: | |
| 70 source = utils.relative_path(settings._destination, | |
| 71 settings._source) | |
| 72 text.extend([ | |
| 73 nodes.reference('', 'View document source', | |
| 74 refuri=source), | |
| 75 nodes.Text('.\n')]) | |
| 76 if settings.datestamp: | |
| 77 datestamp = time.strftime(settings.datestamp, time.gmtime()) | |
| 78 text.append(nodes.Text('Generated on: ' + datestamp + '.\n')) | |
| 79 if settings.generator: | |
| 80 text.extend([ | |
| 81 nodes.Text('Generated by '), | |
| 82 nodes.reference('', 'Docutils', refuri= | |
| 83 'http://docutils.sourceforge.net/'), | |
| 84 nodes.Text(' from '), | |
| 85 nodes.reference('', 'reStructuredText', refuri='http://' | |
| 86 'docutils.sourceforge.net/rst.html'), | |
| 87 nodes.Text(' source.\n')]) | |
| 88 return [nodes.paragraph('', '', *text)] | |
| 89 else: | |
| 90 return None | |
| 91 | |
| 92 | |
| 93 class ExposeInternals(Transform): | |
| 94 | |
| 95 """ | |
| 96 Expose internal attributes if ``expose_internals`` setting is set. | |
| 97 """ | |
| 98 | |
| 99 default_priority = 840 | |
| 100 | |
| 101 def not_Text(self, node): | |
| 102 return not isinstance(node, nodes.Text) | |
| 103 | |
| 104 def apply(self): | |
| 105 if self.document.settings.expose_internals: | |
| 106 for node in self.document.traverse(self.not_Text): | |
| 107 for att in self.document.settings.expose_internals: | |
| 108 value = getattr(node, att, None) | |
| 109 if value is not None: | |
| 110 node['internal:' + att] = value | |
| 111 | |
| 112 | |
| 113 class Messages(Transform): | |
| 114 | |
| 115 """ | |
| 116 Place any system messages generated after parsing into a dedicated section | |
| 117 of the document. | |
| 118 """ | |
| 119 | |
| 120 default_priority = 860 | |
| 121 | |
| 122 def apply(self): | |
| 123 unfiltered = self.document.transform_messages | |
| 124 threshold = self.document.reporter.report_level | |
| 125 messages = [] | |
| 126 for msg in unfiltered: | |
| 127 if msg['level'] >= threshold and not msg.parent: | |
| 128 messages.append(msg) | |
| 129 if messages: | |
| 130 section = nodes.section(classes=['system-messages']) | |
| 131 # @@@ get this from the language module? | |
| 132 section += nodes.title('', 'Docutils System Messages') | |
| 133 section += messages | |
| 134 self.document.transform_messages[:] = [] | |
| 135 self.document += section | |
| 136 | |
| 137 | |
| 138 class FilterMessages(Transform): | |
| 139 | |
| 140 """ | |
| 141 Remove system messages below verbosity threshold. | |
| 142 """ | |
| 143 | |
| 144 default_priority = 870 | |
| 145 | |
| 146 def apply(self): | |
| 147 for node in tuple(self.document.traverse(nodes.system_message)): | |
| 148 if node['level'] < self.document.reporter.report_level: | |
| 149 node.parent.remove(node) | |
| 150 | |
| 151 | |
| 152 class TestMessages(Transform): | |
| 153 | |
| 154 """ | |
| 155 Append all post-parse system messages to the end of the document. | |
| 156 | |
| 157 Used for testing purposes. | |
| 158 """ | |
| 159 | |
| 160 default_priority = 880 | |
| 161 | |
| 162 def apply(self): | |
| 163 for msg in self.document.transform_messages: | |
| 164 if not msg.parent: | |
| 165 self.document += msg | |
| 166 | |
| 167 | |
| 168 class StripComments(Transform): | |
| 169 | |
| 170 """ | |
| 171 Remove comment elements from the document tree (only if the | |
| 172 ``strip_comments`` setting is enabled). | |
| 173 """ | |
| 174 | |
| 175 default_priority = 740 | |
| 176 | |
| 177 def apply(self): | |
| 178 if self.document.settings.strip_comments: | |
| 179 for node in tuple(self.document.traverse(nodes.comment)): | |
| 180 node.parent.remove(node) | |
| 181 | |
| 182 | |
| 183 class StripClassesAndElements(Transform): | |
| 184 | |
| 185 """ | |
| 186 Remove from the document tree all elements with classes in | |
| 187 `self.document.settings.strip_elements_with_classes` and all "classes" | |
| 188 attribute values in `self.document.settings.strip_classes`. | |
| 189 """ | |
| 190 | |
| 191 default_priority = 420 | |
| 192 | |
| 193 def apply(self): | |
| 194 if self.document.settings.strip_elements_with_classes: | |
| 195 self.strip_elements = set( | |
| 196 self.document.settings.strip_elements_with_classes) | |
| 197 # Iterate over a tuple as removing the current node | |
| 198 # corrupts the iterator returned by `traverse`: | |
| 199 for node in tuple(self.document.traverse(self.check_classes)): | |
| 200 node.parent.remove(node) | |
| 201 | |
| 202 if not self.document.settings.strip_classes: | |
| 203 return | |
| 204 strip_classes = self.document.settings.strip_classes | |
| 205 for node in self.document.traverse(nodes.Element): | |
| 206 for class_value in strip_classes: | |
| 207 try: | |
| 208 node['classes'].remove(class_value) | |
| 209 except ValueError: | |
| 210 pass | |
| 211 | |
| 212 def check_classes(self, node): | |
| 213 if not isinstance(node, nodes.Element): | |
| 214 return False | |
| 215 for class_value in node['classes'][:]: | |
| 216 if class_value in self.strip_elements: | |
| 217 return True | |
| 218 return False | |
| 219 | |
| 220 | |
| 221 class SmartQuotes(Transform): | |
| 222 | |
| 223 """ | |
| 224 Replace ASCII quotation marks with typographic form. | |
| 225 | |
| 226 Also replace multiple dashes with em-dash/en-dash characters. | |
| 227 """ | |
| 228 | |
| 229 default_priority = 850 | |
| 230 | |
| 231 nodes_to_skip = (nodes.FixedTextElement, nodes.Special) | |
| 232 """Do not apply "smartquotes" to instances of these block-level nodes.""" | |
| 233 | |
| 234 literal_nodes = (nodes.FixedTextElement, nodes.Special, | |
| 235 nodes.image, nodes.literal, nodes.math, | |
| 236 nodes.raw, nodes.problematic) | |
| 237 """Do apply smartquotes to instances of these inline nodes.""" | |
| 238 | |
| 239 smartquotes_action = 'qDe' | |
| 240 """Setting to select smartquote transformations. | |
| 241 | |
| 242 The default 'qDe' educates normal quote characters: (", '), | |
| 243 em- and en-dashes (---, --) and ellipses (...). | |
| 244 """ | |
| 245 | |
| 246 def __init__(self, document, startnode): | |
| 247 Transform.__init__(self, document, startnode=startnode) | |
| 248 self.unsupported_languages = set() | |
| 249 | |
| 250 def get_tokens(self, txtnodes): | |
| 251 # A generator that yields ``(texttype, nodetext)`` tuples for a list | |
| 252 # of "Text" nodes (interface to ``smartquotes.educate_tokens()``). | |
| 253 for node in txtnodes: | |
| 254 if (isinstance(node.parent, self.literal_nodes) | |
| 255 or isinstance(node.parent.parent, self.literal_nodes)): | |
| 256 yield ('literal', unicode(node)) | |
| 257 else: | |
| 258 # SmartQuotes uses backslash escapes instead of null-escapes | |
| 259 txt = re.sub('(?<=\x00)([-\\\'".`])', r'\\\1', unicode(node)) | |
| 260 yield ('plain', txt) | |
| 261 | |
| 262 def apply(self): | |
| 263 smart_quotes = self.document.settings.smart_quotes | |
| 264 if not smart_quotes: | |
| 265 return | |
| 266 try: | |
| 267 alternative = smart_quotes.startswith('alt') | |
| 268 except AttributeError: | |
| 269 alternative = False | |
| 270 | |
| 271 document_language = self.document.settings.language_code | |
| 272 lc_smartquotes = self.document.settings.smartquotes_locales | |
| 273 if lc_smartquotes: | |
| 274 smartquotes.smartchars.quotes.update(dict(lc_smartquotes)) | |
| 275 | |
| 276 # "Educate" quotes in normal text. Handle each block of text | |
| 277 # (TextElement node) as a unit to keep context around inline nodes: | |
| 278 for node in self.document.traverse(nodes.TextElement): | |
| 279 # skip preformatted text blocks and special elements: | |
| 280 if isinstance(node, self.nodes_to_skip): | |
| 281 continue | |
| 282 # nested TextElements are not "block-level" elements: | |
| 283 if isinstance(node.parent, nodes.TextElement): | |
| 284 continue | |
| 285 | |
| 286 # list of text nodes in the "text block": | |
| 287 txtnodes = [txtnode for txtnode in node.traverse(nodes.Text) | |
| 288 if not isinstance(txtnode.parent, | |
| 289 nodes.option_string)] | |
| 290 | |
| 291 # language: use typographical quotes for language "lang" | |
| 292 lang = node.get_language_code(document_language) | |
| 293 # use alternative form if `smart-quotes` setting starts with "alt": | |
| 294 if alternative: | |
| 295 if '-x-altquot' in lang: | |
| 296 lang = lang.replace('-x-altquot', '') | |
| 297 else: | |
| 298 lang += '-x-altquot' | |
| 299 # drop unsupported subtags: | |
| 300 for tag in utils.normalize_language_tag(lang): | |
| 301 if tag in smartquotes.smartchars.quotes: | |
| 302 lang = tag | |
| 303 break | |
| 304 else: # language not supported: (keep ASCII quotes) | |
| 305 if lang not in self.unsupported_languages: | |
| 306 self.document.reporter.warning('No smart quotes ' | |
| 307 'defined for language "%s".'%lang, base_node=node) | |
| 308 self.unsupported_languages.add(lang) | |
| 309 lang = '' | |
| 310 | |
| 311 # Iterator educating quotes in plain text: | |
| 312 # (see "utils/smartquotes.py" for the attribute setting) | |
| 313 teacher = smartquotes.educate_tokens(self.get_tokens(txtnodes), | |
| 314 attr=self.smartquotes_action, language=lang) | |
| 315 | |
| 316 for txtnode, newtext in zip(txtnodes, teacher): | |
| 317 txtnode.parent.replace(txtnode, nodes.Text(newtext, | |
| 318 rawsource=txtnode.rawsource)) | |
| 319 | |
| 320 self.unsupported_languages = set() # reset |
