You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2204 lines
75 KiB

4 years ago
  1. # $Id: nodes.py 7788 2015-02-16 22:10:52Z milde $
  2. # Author: David Goodger <goodger@python.org>
  3. # Maintainer: docutils-develop@lists.sourceforge.net
  4. # Copyright: This module has been placed in the public domain.
  5. """
  6. Docutils document tree element class library.
  7. Classes in CamelCase are abstract base classes or auxiliary classes. The one
  8. exception is `Text`, for a text (PCDATA) node; uppercase is used to
  9. differentiate from element classes. Classes in lower_case_with_underscores
  10. are element classes, matching the XML element generic identifiers in the DTD_.
  11. The position of each node (the level at which it can occur) is significant and
  12. is represented by abstract base classes (`Root`, `Structural`, `Body`,
  13. `Inline`, etc.). Certain transformations will be easier because we can use
  14. ``isinstance(node, base_class)`` to determine the position of the node in the
  15. hierarchy.
  16. .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
  17. """
  18. __docformat__ = 'reStructuredText'
  19. import sys
  20. import os
  21. import re
  22. import warnings
  23. import types
  24. import unicodedata
  25. # ==============================
  26. # Functional Node Base Classes
  27. # ==============================
  28. class Node(object):
  29. """Abstract base class of nodes in a document tree."""
  30. parent = None
  31. """Back-reference to the Node immediately containing this Node."""
  32. document = None
  33. """The `document` node at the root of the tree containing this Node."""
  34. source = None
  35. """Path or description of the input source which generated this Node."""
  36. line = None
  37. """The line number (1-based) of the beginning of this Node in `source`."""
  38. def __bool__(self):
  39. """
  40. Node instances are always true, even if they're empty. A node is more
  41. than a simple container. Its boolean "truth" does not depend on
  42. having one or more subnodes in the doctree.
  43. Use `len()` to check node length. Use `None` to represent a boolean
  44. false value.
  45. """
  46. return True
  47. if sys.version_info < (3,):
  48. # on 2.x, str(node) will be a byte string with Unicode
  49. # characters > 255 escaped; on 3.x this is no longer necessary
  50. def __str__(self):
  51. return str(self).encode('raw_unicode_escape')
  52. def asdom(self, dom=None):
  53. """Return a DOM **fragment** representation of this Node."""
  54. if dom is None:
  55. import xml.dom.minidom as dom
  56. domroot = dom.Document()
  57. return self._dom_node(domroot)
  58. def pformat(self, indent=' ', level=0):
  59. """
  60. Return an indented pseudo-XML representation, for test purposes.
  61. Override in subclasses.
  62. """
  63. raise NotImplementedError
  64. def copy(self):
  65. """Return a copy of self."""
  66. raise NotImplementedError
  67. def deepcopy(self):
  68. """Return a deep copy of self (also copying children)."""
  69. raise NotImplementedError
  70. def setup_child(self, child):
  71. child.parent = self
  72. if self.document:
  73. child.document = self.document
  74. if child.source is None:
  75. child.source = self.document.current_source
  76. if child.line is None:
  77. child.line = self.document.current_line
  78. def walk(self, visitor):
  79. """
  80. Traverse a tree of `Node` objects, calling the
  81. `dispatch_visit()` method of `visitor` when entering each
  82. node. (The `walkabout()` method is similar, except it also
  83. calls the `dispatch_departure()` method before exiting each
  84. node.)
  85. This tree traversal supports limited in-place tree
  86. modifications. Replacing one node with one or more nodes is
  87. OK, as is removing an element. However, if the node removed
  88. or replaced occurs after the current node, the old node will
  89. still be traversed, and any new nodes will not.
  90. Within ``visit`` methods (and ``depart`` methods for
  91. `walkabout()`), `TreePruningException` subclasses may be raised
  92. (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
  93. Parameter `visitor`: A `NodeVisitor` object, containing a
  94. ``visit`` implementation for each `Node` subclass encountered.
  95. Return true if we should stop the traversal.
  96. """
  97. stop = False
  98. visitor.document.reporter.debug(
  99. 'docutils.nodes.Node.walk calling dispatch_visit for %s'
  100. % self.__class__.__name__)
  101. try:
  102. try:
  103. visitor.dispatch_visit(self)
  104. except (SkipChildren, SkipNode):
  105. return stop
  106. except SkipDeparture: # not applicable; ignore
  107. pass
  108. children = self.children
  109. try:
  110. for child in children[:]:
  111. if child.walk(visitor):
  112. stop = True
  113. break
  114. except SkipSiblings:
  115. pass
  116. except StopTraversal:
  117. stop = True
  118. return stop
  119. def walkabout(self, visitor):
  120. """
  121. Perform a tree traversal similarly to `Node.walk()` (which
  122. see), except also call the `dispatch_departure()` method
  123. before exiting each node.
  124. Parameter `visitor`: A `NodeVisitor` object, containing a
  125. ``visit`` and ``depart`` implementation for each `Node`
  126. subclass encountered.
  127. Return true if we should stop the traversal.
  128. """
  129. call_depart = True
  130. stop = False
  131. visitor.document.reporter.debug(
  132. 'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
  133. % self.__class__.__name__)
  134. try:
  135. try:
  136. visitor.dispatch_visit(self)
  137. except SkipNode:
  138. return stop
  139. except SkipDeparture:
  140. call_depart = False
  141. children = self.children
  142. try:
  143. for child in children[:]:
  144. if child.walkabout(visitor):
  145. stop = True
  146. break
  147. except SkipSiblings:
  148. pass
  149. except SkipChildren:
  150. pass
  151. except StopTraversal:
  152. stop = True
  153. if call_depart:
  154. visitor.document.reporter.debug(
  155. 'docutils.nodes.Node.walkabout calling dispatch_departure '
  156. 'for %s' % self.__class__.__name__)
  157. visitor.dispatch_departure(self)
  158. return stop
  159. def _fast_traverse(self, cls):
  160. """Specialized traverse() that only supports instance checks."""
  161. result = []
  162. if isinstance(self, cls):
  163. result.append(self)
  164. for child in self.children:
  165. result.extend(child._fast_traverse(cls))
  166. return result
  167. def _all_traverse(self):
  168. """Specialized traverse() that doesn't check for a condition."""
  169. result = []
  170. result.append(self)
  171. for child in self.children:
  172. result.extend(child._all_traverse())
  173. return result
  174. def traverse(self, condition=None, include_self=True, descend=True,
  175. siblings=False, ascend=False):
  176. """
  177. Return an iterable containing
  178. * self (if include_self is true)
  179. * all descendants in tree traversal order (if descend is true)
  180. * all siblings (if siblings is true) and their descendants (if
  181. also descend is true)
  182. * the siblings of the parent (if ascend is true) and their
  183. descendants (if also descend is true), and so on
  184. If `condition` is not None, the iterable contains only nodes
  185. for which ``condition(node)`` is true. If `condition` is a
  186. node class ``cls``, it is equivalent to a function consisting
  187. of ``return isinstance(node, cls)``.
  188. If ascend is true, assume siblings to be true as well.
  189. For example, given the following tree::
  190. <paragraph>
  191. <emphasis> <--- emphasis.traverse() and
  192. <strong> <--- strong.traverse() are called.
  193. Foo
  194. Bar
  195. <reference name="Baz" refid="baz">
  196. Baz
  197. Then list(emphasis.traverse()) equals ::
  198. [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
  199. and list(strong.traverse(ascend=True)) equals ::
  200. [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
  201. """
  202. if ascend:
  203. siblings=True
  204. # Check for special argument combinations that allow using an
  205. # optimized version of traverse()
  206. if include_self and descend and not siblings:
  207. if condition is None:
  208. return self._all_traverse()
  209. elif isinstance(condition, type):
  210. return self._fast_traverse(condition)
  211. # Check if `condition` is a class (check for TypeType for Python
  212. # implementations that use only new-style classes, like PyPy).
  213. if isinstance(condition, type):
  214. node_class = condition
  215. def condition(node, node_class=node_class):
  216. return isinstance(node, node_class)
  217. r = []
  218. if include_self and (condition is None or condition(self)):
  219. r.append(self)
  220. if descend and len(self.children):
  221. for child in self:
  222. r.extend(child.traverse(include_self=True, descend=True,
  223. siblings=False, ascend=False,
  224. condition=condition))
  225. if siblings or ascend:
  226. node = self
  227. while node.parent:
  228. index = node.parent.index(node)
  229. for sibling in node.parent[index+1:]:
  230. r.extend(sibling.traverse(include_self=True,
  231. descend=descend,
  232. siblings=False, ascend=False,
  233. condition=condition))
  234. if not ascend:
  235. break
  236. else:
  237. node = node.parent
  238. return r
  239. def next_node(self, condition=None, include_self=False, descend=True,
  240. siblings=False, ascend=False):
  241. """
  242. Return the first node in the iterable returned by traverse(),
  243. or None if the iterable is empty.
  244. Parameter list is the same as of traverse. Note that
  245. include_self defaults to 0, though.
  246. """
  247. iterable = self.traverse(condition=condition,
  248. include_self=include_self, descend=descend,
  249. siblings=siblings, ascend=ascend)
  250. try:
  251. return iterable[0]
  252. except IndexError:
  253. return None
  254. if sys.version_info < (3,):
  255. class reprunicode(str):
  256. """
  257. A unicode sub-class that removes the initial u from unicode's repr.
  258. """
  259. def __repr__(self):
  260. return str.__repr__(self)[1:]
  261. else:
  262. reprunicode = str
  263. def ensure_str(s):
  264. """
  265. Failsave conversion of `unicode` to `str`.
  266. """
  267. if sys.version_info < (3,) and isinstance(s, str):
  268. return s.encode('ascii', 'backslashreplace')
  269. return s
  270. class Text(Node, reprunicode):
  271. """
  272. Instances are terminal nodes (leaves) containing text only; no child
  273. nodes or attributes. Initialize by passing a string to the constructor.
  274. Access the text itself with the `astext` method.
  275. """
  276. tagname = '#text'
  277. children = ()
  278. """Text nodes have no children, and cannot have children."""
  279. if sys.version_info > (3,):
  280. def __new__(cls, data, rawsource=None):
  281. """Prevent the rawsource argument from propagating to str."""
  282. if isinstance(data, bytes):
  283. raise TypeError('expecting str data, not bytes')
  284. return reprunicode.__new__(cls, data)
  285. else:
  286. def __new__(cls, data, rawsource=None):
  287. """Prevent the rawsource argument from propagating to str."""
  288. return reprunicode.__new__(cls, data)
  289. def __init__(self, data, rawsource=''):
  290. self.rawsource = rawsource
  291. """The raw text from which this element was constructed."""
  292. def shortrepr(self, maxlen=18):
  293. data = self
  294. if len(data) > maxlen:
  295. data = data[:maxlen-4] + ' ...'
  296. return '<%s: %r>' % (self.tagname, reprunicode(data))
  297. def __repr__(self):
  298. return self.shortrepr(maxlen=68)
  299. def _dom_node(self, domroot):
  300. return domroot.createTextNode(str(self))
  301. def astext(self):
  302. return reprunicode(self)
  303. # Note about __unicode__: The implementation of __unicode__ here,
  304. # and the one raising NotImplemented in the superclass Node had
  305. # to be removed when changing Text to a subclass of unicode instead
  306. # of UserString, since there is no way to delegate the __unicode__
  307. # call to the superclass unicode:
  308. # unicode itself does not have __unicode__ method to delegate to
  309. # and calling unicode(self) or unicode.__new__ directly creates
  310. # an infinite loop
  311. def copy(self):
  312. return self.__class__(reprunicode(self), rawsource=self.rawsource)
  313. def deepcopy(self):
  314. return self.copy()
  315. def pformat(self, indent=' ', level=0):
  316. result = []
  317. indent = indent * level
  318. for line in self.splitlines():
  319. result.append(indent + line + '\n')
  320. return ''.join(result)
  321. # rstrip and lstrip are used by substitution definitions where
  322. # they are expected to return a Text instance, this was formerly
  323. # taken care of by UserString. Note that then and now the
  324. # rawsource member is lost.
  325. def rstrip(self, chars=None):
  326. return self.__class__(reprunicode.rstrip(self, chars))
  327. def lstrip(self, chars=None):
  328. return self.__class__(reprunicode.lstrip(self, chars))
  329. class Element(Node):
  330. """
  331. `Element` is the superclass to all specific elements.
  332. Elements contain attributes and child nodes. Elements emulate
  333. dictionaries for attributes, indexing by attribute name (a string). To
  334. set the attribute 'att' to 'value', do::
  335. element['att'] = 'value'
  336. There are two special attributes: 'ids' and 'names'. Both are
  337. lists of unique identifiers, and names serve as human interfaces
  338. to IDs. Names are case- and whitespace-normalized (see the
  339. fully_normalize_name() function), and IDs conform to the regular
  340. expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
  341. Elements also emulate lists for child nodes (element nodes and/or text
  342. nodes), indexing by integer. To get the first child node, use::
  343. element[0]
  344. Elements may be constructed using the ``+=`` operator. To add one new
  345. child node to element, do::
  346. element += node
  347. This is equivalent to ``element.append(node)``.
  348. To add a list of multiple child nodes at once, use the same ``+=``
  349. operator::
  350. element += [node1, node2]
  351. This is equivalent to ``element.extend([node1, node2])``.
  352. """
  353. basic_attributes = ('ids', 'classes', 'names', 'dupnames')
  354. """List attributes which are defined for every Element-derived class
  355. instance and can be safely transferred to a different node."""
  356. local_attributes = ('backrefs',)
  357. """A list of class-specific attributes that should not be copied with the
  358. standard attributes when replacing a node.
  359. NOTE: Derived classes should override this value to prevent any of its
  360. attributes being copied by adding to the value in its parent class."""
  361. list_attributes = basic_attributes + local_attributes
  362. """List attributes, automatically initialized to empty lists for
  363. all nodes."""
  364. known_attributes = list_attributes + ('source',)
  365. """List attributes that are known to the Element base class."""
  366. tagname = None
  367. """The element generic identifier. If None, it is set as an instance
  368. attribute to the name of the class."""
  369. child_text_separator = '\n\n'
  370. """Separator for child nodes, used by `astext()` method."""
  371. def __init__(self, rawsource='', *children, **attributes):
  372. self.rawsource = rawsource
  373. """The raw text from which this element was constructed."""
  374. self.children = []
  375. """List of child nodes (elements and/or `Text`)."""
  376. self.extend(children) # maintain parent info
  377. self.attributes = {}
  378. """Dictionary of attribute {name: value}."""
  379. # Initialize list attributes.
  380. for att in self.list_attributes:
  381. self.attributes[att] = []
  382. for att, value in list(attributes.items()):
  383. att = att.lower()
  384. if att in self.list_attributes:
  385. # mutable list; make a copy for this node
  386. self.attributes[att] = value[:]
  387. else:
  388. self.attributes[att] = value
  389. if self.tagname is None:
  390. self.tagname = self.__class__.__name__
  391. def _dom_node(self, domroot):
  392. element = domroot.createElement(self.tagname)
  393. for attribute, value in self.attlist():
  394. if isinstance(value, list):
  395. value = ' '.join([serial_escape('%s' % (v,)) for v in value])
  396. element.setAttribute(attribute, '%s' % value)
  397. for child in self.children:
  398. element.appendChild(child._dom_node(domroot))
  399. return element
  400. def __repr__(self):
  401. data = ''
  402. for c in self.children:
  403. data += c.shortrepr()
  404. if len(data) > 60:
  405. data = data[:56] + ' ...'
  406. break
  407. if self['names']:
  408. return '<%s "%s": %s>' % (self.__class__.__name__,
  409. '; '.join([ensure_str(n) for n in self['names']]), data)
  410. else:
  411. return '<%s: %s>' % (self.__class__.__name__, data)
  412. def shortrepr(self):
  413. if self['names']:
  414. return '<%s "%s"...>' % (self.__class__.__name__,
  415. '; '.join([ensure_str(n) for n in self['names']]))
  416. else:
  417. return '<%s...>' % self.tagname
  418. def __unicode__(self):
  419. if self.children:
  420. return '%s%s%s' % (self.starttag(),
  421. ''.join([str(c) for c in self.children]),
  422. self.endtag())
  423. else:
  424. return self.emptytag()
  425. if sys.version_info > (3,):
  426. # 2to3 doesn't convert __unicode__ to __str__
  427. __str__ = __unicode__
  428. def starttag(self, quoteattr=None):
  429. # the optional arg is used by the docutils_xml writer
  430. if quoteattr is None:
  431. quoteattr = pseudo_quoteattr
  432. parts = [self.tagname]
  433. for name, value in self.attlist():
  434. if value is None: # boolean attribute
  435. parts.append('%s="True"' % name)
  436. continue
  437. if isinstance(value, list):
  438. values = [serial_escape('%s' % (v,)) for v in value]
  439. value = ' '.join(values)
  440. else:
  441. value = str(value)
  442. value = quoteattr(value)
  443. parts.append('%s=%s' % (name, value))
  444. return '<%s>' % ' '.join(parts)
  445. def endtag(self):
  446. return '</%s>' % self.tagname
  447. def emptytag(self):
  448. return '<%s/>' % ' '.join([self.tagname] +
  449. ['%s="%s"' % (n, v)
  450. for n, v in self.attlist()])
  451. def __len__(self):
  452. return len(self.children)
  453. def __contains__(self, key):
  454. # support both membership test for children and attributes
  455. # (has_key is translated to "in" by 2to3)
  456. if isinstance(key, str):
  457. return key in self.attributes
  458. return key in self.children
  459. def __getitem__(self, key):
  460. if isinstance(key, str):
  461. return self.attributes[key]
  462. elif isinstance(key, int):
  463. return self.children[key]
  464. elif isinstance(key, slice):
  465. assert key.step in (None, 1), 'cannot handle slice with stride'
  466. return self.children[key.start:key.stop]
  467. else:
  468. raise TypeError('element index must be an integer, a slice, or '
  469. 'an attribute name string')
  470. def __setitem__(self, key, item):
  471. if isinstance(key, str):
  472. self.attributes[str(key)] = item
  473. elif isinstance(key, int):
  474. self.setup_child(item)
  475. self.children[key] = item
  476. elif isinstance(key, slice):
  477. assert key.step in (None, 1), 'cannot handle slice with stride'
  478. for node in item:
  479. self.setup_child(node)
  480. self.children[key.start:key.stop] = item
  481. else:
  482. raise TypeError('element index must be an integer, a slice, or '
  483. 'an attribute name string')
  484. def __delitem__(self, key):
  485. if isinstance(key, str):
  486. del self.attributes[key]
  487. elif isinstance(key, int):
  488. del self.children[key]
  489. elif isinstance(key, slice):
  490. assert key.step in (None, 1), 'cannot handle slice with stride'
  491. del self.children[key.start:key.stop]
  492. else:
  493. raise TypeError('element index must be an integer, a simple '
  494. 'slice, or an attribute name string')
  495. def __add__(self, other):
  496. return self.children + other
  497. def __radd__(self, other):
  498. return other + self.children
  499. def __iadd__(self, other):
  500. """Append a node or a list of nodes to `self.children`."""
  501. if isinstance(other, Node):
  502. self.append(other)
  503. elif other is not None:
  504. self.extend(other)
  505. return self
  506. def astext(self):
  507. return self.child_text_separator.join(
  508. [child.astext() for child in self.children])
  509. def non_default_attributes(self):
  510. atts = {}
  511. for key, value in list(self.attributes.items()):
  512. if self.is_not_default(key):
  513. atts[key] = value
  514. return atts
  515. def attlist(self):
  516. attlist = list(self.non_default_attributes().items())
  517. attlist.sort()
  518. return attlist
  519. def get(self, key, failobj=None):
  520. return self.attributes.get(key, failobj)
  521. def hasattr(self, attr):
  522. return attr in self.attributes
  523. def delattr(self, attr):
  524. if attr in self.attributes:
  525. del self.attributes[attr]
  526. def setdefault(self, key, failobj=None):
  527. return self.attributes.setdefault(key, failobj)
  528. has_key = hasattr
  529. # support operator ``in``
  530. __contains__ = hasattr
  531. def get_language_code(self, fallback=''):
  532. """Return node's language tag.
  533. Look iteratively in self and parents for a class argument
  534. starting with ``language-`` and return the remainder of it
  535. (which should be a `BCP49` language tag) or the `fallback`.
  536. """
  537. for cls in self.get('classes', []):
  538. if cls.startswith('language-'):
  539. return cls[9:]
  540. try:
  541. return self.parent.get_language(fallback)
  542. except AttributeError:
  543. return fallback
  544. def append(self, item):
  545. self.setup_child(item)
  546. self.children.append(item)
  547. def extend(self, item):
  548. for node in item:
  549. self.append(node)
  550. def insert(self, index, item):
  551. if isinstance(item, Node):
  552. self.setup_child(item)
  553. self.children.insert(index, item)
  554. elif item is not None:
  555. self[index:index] = item
  556. def pop(self, i=-1):
  557. return self.children.pop(i)
  558. def remove(self, item):
  559. self.children.remove(item)
  560. def index(self, item):
  561. return self.children.index(item)
  562. def is_not_default(self, key):
  563. if self[key] == [] and key in self.list_attributes:
  564. return 0
  565. else:
  566. return 1
  567. def update_basic_atts(self, dict_):
  568. """
  569. Update basic attributes ('ids', 'names', 'classes',
  570. 'dupnames', but not 'source') from node or dictionary `dict_`.
  571. """
  572. if isinstance(dict_, Node):
  573. dict_ = dict_.attributes
  574. for att in self.basic_attributes:
  575. self.append_attr_list(att, dict_.get(att, []))
  576. def append_attr_list(self, attr, values):
  577. """
  578. For each element in values, if it does not exist in self[attr], append
  579. it.
  580. NOTE: Requires self[attr] and values to be sequence type and the
  581. former should specifically be a list.
  582. """
  583. # List Concatenation
  584. for value in values:
  585. if not value in self[attr]:
  586. self[attr].append(value)
  587. def coerce_append_attr_list(self, attr, value):
  588. """
  589. First, convert both self[attr] and value to a non-string sequence
  590. type; if either is not already a sequence, convert it to a list of one
  591. element. Then call append_attr_list.
  592. NOTE: self[attr] and value both must not be None.
  593. """
  594. # List Concatenation
  595. if not isinstance(self.get(attr), list):
  596. self[attr] = [self[attr]]
  597. if not isinstance(value, list):
  598. value = [value]
  599. self.append_attr_list(attr, value)
  600. def replace_attr(self, attr, value, force = True):
  601. """
  602. If self[attr] does not exist or force is True or omitted, set
  603. self[attr] to value, otherwise do nothing.
  604. """
  605. # One or the other
  606. if force or self.get(attr) is None:
  607. self[attr] = value
  608. def copy_attr_convert(self, attr, value, replace = True):
  609. """
  610. If attr is an attribute of self, set self[attr] to
  611. [self[attr], value], otherwise set self[attr] to value.
  612. NOTE: replace is not used by this function and is kept only for
  613. compatibility with the other copy functions.
  614. """
  615. if self.get(attr) is not value:
  616. self.coerce_append_attr_list(attr, value)
  617. def copy_attr_coerce(self, attr, value, replace):
  618. """
  619. If attr is an attribute of self and either self[attr] or value is a
  620. list, convert all non-sequence values to a sequence of 1 element and
  621. then concatenate the two sequence, setting the result to self[attr].
  622. If both self[attr] and value are non-sequences and replace is True or
  623. self[attr] is None, replace self[attr] with value. Otherwise, do
  624. nothing.
  625. """
  626. if self.get(attr) is not value:
  627. if isinstance(self.get(attr), list) or \
  628. isinstance(value, list):
  629. self.coerce_append_attr_list(attr, value)
  630. else:
  631. self.replace_attr(attr, value, replace)
  632. def copy_attr_concatenate(self, attr, value, replace):
  633. """
  634. If attr is an attribute of self and both self[attr] and value are
  635. lists, concatenate the two sequences, setting the result to
  636. self[attr]. If either self[attr] or value are non-sequences and
  637. replace is True or self[attr] is None, replace self[attr] with value.
  638. Otherwise, do nothing.
  639. """
  640. if self.get(attr) is not value:
  641. if isinstance(self.get(attr), list) and \
  642. isinstance(value, list):
  643. self.append_attr_list(attr, value)
  644. else:
  645. self.replace_attr(attr, value, replace)
  646. def copy_attr_consistent(self, attr, value, replace):
  647. """
  648. If replace is True or selfpattr] is None, replace self[attr] with
  649. value. Otherwise, do nothing.
  650. """
  651. if self.get(attr) is not value:
  652. self.replace_attr(attr, value, replace)
  653. def update_all_atts(self, dict_, update_fun = copy_attr_consistent,
  654. replace = True, and_source = False):
  655. """
  656. Updates all attributes from node or dictionary `dict_`.
  657. Appends the basic attributes ('ids', 'names', 'classes',
  658. 'dupnames', but not 'source') and then, for all other attributes in
  659. dict_, updates the same attribute in self. When attributes with the
  660. same identifier appear in both self and dict_, the two values are
  661. merged based on the value of update_fun. Generally, when replace is
  662. True, the values in self are replaced or merged with the values in
  663. dict_; otherwise, the values in self may be preserved or merged. When
  664. and_source is True, the 'source' attribute is included in the copy.
  665. NOTE: When replace is False, and self contains a 'source' attribute,
  666. 'source' is not replaced even when dict_ has a 'source'
  667. attribute, though it may still be merged into a list depending
  668. on the value of update_fun.
  669. NOTE: It is easier to call the update-specific methods then to pass
  670. the update_fun method to this function.
  671. """
  672. if isinstance(dict_, Node):
  673. dict_ = dict_.attributes
  674. # Include the source attribute when copying?
  675. if and_source:
  676. filter_fun = self.is_not_list_attribute
  677. else:
  678. filter_fun = self.is_not_known_attribute
  679. # Copy the basic attributes
  680. self.update_basic_atts(dict_)
  681. # Grab other attributes in dict_ not in self except the
  682. # (All basic attributes should be copied already)
  683. for att in filter(filter_fun, dict_):
  684. update_fun(self, att, dict_[att], replace)
  685. def update_all_atts_consistantly(self, dict_, replace = True,
  686. and_source = False):
  687. """
  688. Updates all attributes from node or dictionary `dict_`.
  689. Appends the basic attributes ('ids', 'names', 'classes',
  690. 'dupnames', but not 'source') and then, for all other attributes in
  691. dict_, updates the same attribute in self. When attributes with the
  692. same identifier appear in both self and dict_ and replace is True, the
  693. values in self are replaced with the values in dict_; otherwise, the
  694. values in self are preserved. When and_source is True, the 'source'
  695. attribute is included in the copy.
  696. NOTE: When replace is False, and self contains a 'source' attribute,
  697. 'source' is not replaced even when dict_ has a 'source'
  698. attribute, though it may still be merged into a list depending
  699. on the value of update_fun.
  700. """
  701. self.update_all_atts(dict_, Element.copy_attr_consistent, replace,
  702. and_source)
  703. def update_all_atts_concatenating(self, dict_, replace = True,
  704. and_source = False):
  705. """
  706. Updates all attributes from node or dictionary `dict_`.
  707. Appends the basic attributes ('ids', 'names', 'classes',
  708. 'dupnames', but not 'source') and then, for all other attributes in
  709. dict_, updates the same attribute in self. When attributes with the
  710. same identifier appear in both self and dict_ whose values aren't each
  711. lists and replace is True, the values in self are replaced with the
  712. values in dict_; if the values from self and dict_ for the given
  713. identifier are both of list type, then the two lists are concatenated
  714. and the result stored in self; otherwise, the values in self are
  715. preserved. When and_source is True, the 'source' attribute is
  716. included in the copy.
  717. NOTE: When replace is False, and self contains a 'source' attribute,
  718. 'source' is not replaced even when dict_ has a 'source'
  719. attribute, though it may still be merged into a list depending
  720. on the value of update_fun.
  721. """
  722. self.update_all_atts(dict_, Element.copy_attr_concatenate, replace,
  723. and_source)
  724. def update_all_atts_coercion(self, dict_, replace = True,
  725. and_source = False):
  726. """
  727. Updates all attributes from node or dictionary `dict_`.
  728. Appends the basic attributes ('ids', 'names', 'classes',
  729. 'dupnames', but not 'source') and then, for all other attributes in
  730. dict_, updates the same attribute in self. When attributes with the
  731. same identifier appear in both self and dict_ whose values are both
  732. not lists and replace is True, the values in self are replaced with
  733. the values in dict_; if either of the values from self and dict_ for
  734. the given identifier are of list type, then first any non-lists are
  735. converted to 1-element lists and then the two lists are concatenated
  736. and the result stored in self; otherwise, the values in self are
  737. preserved. When and_source is True, the 'source' attribute is
  738. included in the copy.
  739. NOTE: When replace is False, and self contains a 'source' attribute,
  740. 'source' is not replaced even when dict_ has a 'source'
  741. attribute, though it may still be merged into a list depending
  742. on the value of update_fun.
  743. """
  744. self.update_all_atts(dict_, Element.copy_attr_coerce, replace,
  745. and_source)
  746. def update_all_atts_convert(self, dict_, and_source = False):
  747. """
  748. Updates all attributes from node or dictionary `dict_`.
  749. Appends the basic attributes ('ids', 'names', 'classes',
  750. 'dupnames', but not 'source') and then, for all other attributes in
  751. dict_, updates the same attribute in self. When attributes with the
  752. same identifier appear in both self and dict_ then first any non-lists
  753. are converted to 1-element lists and then the two lists are
  754. concatenated and the result stored in self; otherwise, the values in
  755. self are preserved. When and_source is True, the 'source' attribute
  756. is included in the copy.
  757. NOTE: When replace is False, and self contains a 'source' attribute,
  758. 'source' is not replaced even when dict_ has a 'source'
  759. attribute, though it may still be merged into a list depending
  760. on the value of update_fun.
  761. """
  762. self.update_all_atts(dict_, Element.copy_attr_convert,
  763. and_source = and_source)
  764. def clear(self):
  765. self.children = []
  766. def replace(self, old, new):
  767. """Replace one child `Node` with another child or children."""
  768. index = self.index(old)
  769. if isinstance(new, Node):
  770. self.setup_child(new)
  771. self[index] = new
  772. elif new is not None:
  773. self[index:index+1] = new
  774. def replace_self(self, new):
  775. """
  776. Replace `self` node with `new`, where `new` is a node or a
  777. list of nodes.
  778. """
  779. update = new
  780. if not isinstance(new, Node):
  781. # `new` is a list; update first child.
  782. try:
  783. update = new[0]
  784. except IndexError:
  785. update = None
  786. if isinstance(update, Element):
  787. update.update_basic_atts(self)
  788. else:
  789. # `update` is a Text node or `new` is an empty list.
  790. # Assert that we aren't losing any attributes.
  791. for att in self.basic_attributes:
  792. assert not self[att], \
  793. 'Losing "%s" attribute: %s' % (att, self[att])
  794. self.parent.replace(self, new)
  795. def first_child_matching_class(self, childclass, start=0, end=sys.maxsize):
  796. """
  797. Return the index of the first child whose class exactly matches.
  798. Parameters:
  799. - `childclass`: A `Node` subclass to search for, or a tuple of `Node`
  800. classes. If a tuple, any of the classes may match.
  801. - `start`: Initial index to check.
  802. - `end`: Initial index to *not* check.
  803. """
  804. if not isinstance(childclass, tuple):
  805. childclass = (childclass,)
  806. for index in range(start, min(len(self), end)):
  807. for c in childclass:
  808. if isinstance(self[index], c):
  809. return index
  810. return None
  811. def first_child_not_matching_class(self, childclass, start=0,
  812. end=sys.maxsize):
  813. """
  814. Return the index of the first child whose class does *not* match.
  815. Parameters:
  816. - `childclass`: A `Node` subclass to skip, or a tuple of `Node`
  817. classes. If a tuple, none of the classes may match.
  818. - `start`: Initial index to check.
  819. - `end`: Initial index to *not* check.
  820. """
  821. if not isinstance(childclass, tuple):
  822. childclass = (childclass,)
  823. for index in range(start, min(len(self), end)):
  824. for c in childclass:
  825. if isinstance(self.children[index], c):
  826. break
  827. else:
  828. return index
  829. return None
  830. def pformat(self, indent=' ', level=0):
  831. return ''.join(['%s%s\n' % (indent * level, self.starttag())] +
  832. [child.pformat(indent, level+1)
  833. for child in self.children])
  834. def copy(self):
  835. return self.__class__(rawsource=self.rawsource, **self.attributes)
  836. def deepcopy(self):
  837. copy = self.copy()
  838. copy.extend([child.deepcopy() for child in self.children])
  839. return copy
  840. def set_class(self, name):
  841. """Add a new class to the "classes" attribute."""
  842. warnings.warn('docutils.nodes.Element.set_class deprecated; '
  843. "append to Element['classes'] list attribute directly",
  844. DeprecationWarning, stacklevel=2)
  845. assert ' ' not in name
  846. self['classes'].append(name.lower())
  847. def note_referenced_by(self, name=None, id=None):
  848. """Note that this Element has been referenced by its name
  849. `name` or id `id`."""
  850. self.referenced = 1
  851. # Element.expect_referenced_by_* dictionaries map names or ids
  852. # to nodes whose ``referenced`` attribute is set to true as
  853. # soon as this node is referenced by the given name or id.
  854. # Needed for target propagation.
  855. by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
  856. by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
  857. if by_name:
  858. assert name is not None
  859. by_name.referenced = 1
  860. if by_id:
  861. assert id is not None
  862. by_id.referenced = 1
  863. @classmethod
  864. def is_not_list_attribute(cls, attr):
  865. """
  866. Returns True if and only if the given attribute is NOT one of the
  867. basic list attributes defined for all Elements.
  868. """
  869. return attr not in cls.list_attributes
  870. @classmethod
  871. def is_not_known_attribute(cls, attr):
  872. """
  873. Returns True if and only if the given attribute is NOT recognized by
  874. this class.
  875. """
  876. return attr not in cls.known_attributes
  877. class TextElement(Element):
  878. """
  879. An element which directly contains text.
  880. Its children are all `Text` or `Inline` subclass nodes. You can
  881. check whether an element's context is inline simply by checking whether
  882. its immediate parent is a `TextElement` instance (including subclasses).
  883. This is handy for nodes like `image` that can appear both inline and as
  884. standalone body elements.
  885. If passing children to `__init__()`, make sure to set `text` to
  886. ``''`` or some other suitable value.
  887. """
  888. child_text_separator = ''
  889. """Separator for child nodes, used by `astext()` method."""
  890. def __init__(self, rawsource='', text='', *children, **attributes):
  891. if text != '':
  892. textnode = Text(text)
  893. Element.__init__(self, rawsource, textnode, *children,
  894. **attributes)
  895. else:
  896. Element.__init__(self, rawsource, *children, **attributes)
  897. class FixedTextElement(TextElement):
  898. """An element which directly contains preformatted text."""
  899. def __init__(self, rawsource='', text='', *children, **attributes):
  900. TextElement.__init__(self, rawsource, text, *children, **attributes)
  901. self.attributes['xml:space'] = 'preserve'
  902. # ========
  903. # Mixins
  904. # ========
  905. class Resolvable:
  906. resolved = 0
  907. class BackLinkable:
  908. def add_backref(self, refid):
  909. self['backrefs'].append(refid)
  910. # ====================
  911. # Element Categories
  912. # ====================
  913. class Root: pass
  914. class Titular: pass
  915. class PreBibliographic:
  916. """Category of Node which may occur before Bibliographic Nodes."""
  917. class Bibliographic: pass
  918. class Decorative(PreBibliographic): pass
  919. class Structural: pass
  920. class Body: pass
  921. class General(Body): pass
  922. class Sequential(Body):
  923. """List-like elements."""
  924. class Admonition(Body): pass
  925. class Special(Body):
  926. """Special internal body elements."""
  927. class Invisible(PreBibliographic):
  928. """Internal elements that don't appear in output."""
  929. class Part: pass
  930. class Inline: pass
  931. class Referential(Resolvable): pass
  932. class Targetable(Resolvable):
  933. referenced = 0
  934. indirect_reference_name = None
  935. """Holds the whitespace_normalized_name (contains mixed case) of a target.
  936. Required for MoinMoin/reST compatibility."""
  937. class Labeled:
  938. """Contains a `label` as its first element."""
  939. # ==============
  940. # Root Element
  941. # ==============
  942. class document(Root, Structural, Element):
  943. """
  944. The document root element.
  945. Do not instantiate this class directly; use
  946. `docutils.utils.new_document()` instead.
  947. """
  948. def __init__(self, settings, reporter, *args, **kwargs):
  949. Element.__init__(self, *args, **kwargs)
  950. self.current_source = None
  951. """Path to or description of the input source being processed."""
  952. self.current_line = None
  953. """Line number (1-based) of `current_source`."""
  954. self.settings = settings
  955. """Runtime settings data record."""
  956. self.reporter = reporter
  957. """System message generator."""
  958. self.indirect_targets = []
  959. """List of indirect target nodes."""
  960. self.substitution_defs = {}
  961. """Mapping of substitution names to substitution_definition nodes."""
  962. self.substitution_names = {}
  963. """Mapping of case-normalized substitution names to case-sensitive
  964. names."""
  965. self.refnames = {}
  966. """Mapping of names to lists of referencing nodes."""
  967. self.refids = {}
  968. """Mapping of ids to lists of referencing nodes."""
  969. self.nameids = {}
  970. """Mapping of names to unique id's."""
  971. self.nametypes = {}
  972. """Mapping of names to hyperlink type (boolean: True => explicit,
  973. False => implicit."""
  974. self.ids = {}
  975. """Mapping of ids to nodes."""
  976. self.footnote_refs = {}
  977. """Mapping of footnote labels to lists of footnote_reference nodes."""
  978. self.citation_refs = {}
  979. """Mapping of citation labels to lists of citation_reference nodes."""
  980. self.autofootnotes = []
  981. """List of auto-numbered footnote nodes."""
  982. self.autofootnote_refs = []
  983. """List of auto-numbered footnote_reference nodes."""
  984. self.symbol_footnotes = []
  985. """List of symbol footnote nodes."""
  986. self.symbol_footnote_refs = []
  987. """List of symbol footnote_reference nodes."""
  988. self.footnotes = []
  989. """List of manually-numbered footnote nodes."""
  990. self.citations = []
  991. """List of citation nodes."""
  992. self.autofootnote_start = 1
  993. """Initial auto-numbered footnote number."""
  994. self.symbol_footnote_start = 0
  995. """Initial symbol footnote symbol index."""
  996. self.id_start = 1
  997. """Initial ID number."""
  998. self.parse_messages = []
  999. """System messages generated while parsing."""
  1000. self.transform_messages = []
  1001. """System messages generated while applying transforms."""
  1002. import docutils.transforms
  1003. self.transformer = docutils.transforms.Transformer(self)
  1004. """Storage for transforms to be applied to this document."""
  1005. self.decoration = None
  1006. """Document's `decoration` node."""
  1007. self.document = self
  1008. def __getstate__(self):
  1009. """
  1010. Return dict with unpicklable references removed.
  1011. """
  1012. state = self.__dict__.copy()
  1013. state['reporter'] = None
  1014. state['transformer'] = None
  1015. return state
  1016. def asdom(self, dom=None):
  1017. """Return a DOM representation of this document."""
  1018. if dom is None:
  1019. import xml.dom.minidom as dom
  1020. domroot = dom.Document()
  1021. domroot.appendChild(self._dom_node(domroot))
  1022. return domroot
  1023. def set_id(self, node, msgnode=None):
  1024. for id in node['ids']:
  1025. if id in self.ids and self.ids[id] is not node:
  1026. msg = self.reporter.severe('Duplicate ID: "%s".' % id)
  1027. if msgnode != None:
  1028. msgnode += msg
  1029. if not node['ids']:
  1030. for name in node['names']:
  1031. id = self.settings.id_prefix + make_id(name)
  1032. if id and id not in self.ids:
  1033. break
  1034. else:
  1035. id = ''
  1036. while not id or id in self.ids:
  1037. id = (self.settings.id_prefix +
  1038. self.settings.auto_id_prefix + str(self.id_start))
  1039. self.id_start += 1
  1040. node['ids'].append(id)
  1041. self.ids[id] = node
  1042. return id
  1043. def set_name_id_map(self, node, id, msgnode=None, explicit=None):
  1044. """
  1045. `self.nameids` maps names to IDs, while `self.nametypes` maps names to
  1046. booleans representing hyperlink type (True==explicit,
  1047. False==implicit). This method updates the mappings.
  1048. The following state transition table shows how `self.nameids` ("ids")
  1049. and `self.nametypes` ("types") change with new input (a call to this
  1050. method), and what actions are performed ("implicit"-type system
  1051. messages are INFO/1, and "explicit"-type system messages are ERROR/3):
  1052. ==== ===== ======== ======== ======= ==== ===== =====
  1053. Old State Input Action New State Notes
  1054. ----------- -------- ----------------- ----------- -----
  1055. ids types new type sys.msg. dupname ids types
  1056. ==== ===== ======== ======== ======= ==== ===== =====
  1057. - - explicit - - new True
  1058. - - implicit - - new False
  1059. None False explicit - - new True
  1060. old False explicit implicit old new True
  1061. None True explicit explicit new None True
  1062. old True explicit explicit new,old None True [#]_
  1063. None False implicit implicit new None False
  1064. old False implicit implicit new,old None False
  1065. None True implicit implicit new None True
  1066. old True implicit implicit new old True
  1067. ==== ===== ======== ======== ======= ==== ===== =====
  1068. .. [#] Do not clear the name-to-id map or invalidate the old target if
  1069. both old and new targets are external and refer to identical URIs.
  1070. The new target is invalidated regardless.
  1071. """
  1072. for name in node['names']:
  1073. if name in self.nameids:
  1074. self.set_duplicate_name_id(node, id, name, msgnode, explicit)
  1075. else:
  1076. self.nameids[name] = id
  1077. self.nametypes[name] = explicit
  1078. def set_duplicate_name_id(self, node, id, name, msgnode, explicit):
  1079. old_id = self.nameids[name]
  1080. old_explicit = self.nametypes[name]
  1081. self.nametypes[name] = old_explicit or explicit
  1082. if explicit:
  1083. if old_explicit:
  1084. level = 2
  1085. if old_id is not None:
  1086. old_node = self.ids[old_id]
  1087. if 'refuri' in node:
  1088. refuri = node['refuri']
  1089. if old_node['names'] \
  1090. and 'refuri' in old_node \
  1091. and old_node['refuri'] == refuri:
  1092. level = 1 # just inform if refuri's identical
  1093. if level > 1:
  1094. dupname(old_node, name)
  1095. self.nameids[name] = None
  1096. msg = self.reporter.system_message(
  1097. level, 'Duplicate explicit target name: "%s".' % name,
  1098. backrefs=[id], base_node=node)
  1099. if msgnode != None:
  1100. msgnode += msg
  1101. dupname(node, name)
  1102. else:
  1103. self.nameids[name] = id
  1104. if old_id is not None:
  1105. old_node = self.ids[old_id]
  1106. dupname(old_node, name)
  1107. else:
  1108. if old_id is not None and not old_explicit:
  1109. self.nameids[name] = None
  1110. old_node = self.ids[old_id]
  1111. dupname(old_node, name)
  1112. dupname(node, name)
  1113. if not explicit or (not old_explicit and old_id is not None):
  1114. msg = self.reporter.info(
  1115. 'Duplicate implicit target name: "%s".' % name,
  1116. backrefs=[id], base_node=node)
  1117. if msgnode != None:
  1118. msgnode += msg
  1119. def has_name(self, name):
  1120. return name in self.nameids
  1121. # "note" here is an imperative verb: "take note of".
  1122. def note_implicit_target(self, target, msgnode=None):
  1123. id = self.set_id(target, msgnode)
  1124. self.set_name_id_map(target, id, msgnode, explicit=None)
  1125. def note_explicit_target(self, target, msgnode=None):
  1126. id = self.set_id(target, msgnode)
  1127. self.set_name_id_map(target, id, msgnode, explicit=True)
  1128. def note_refname(self, node):
  1129. self.refnames.setdefault(node['refname'], []).append(node)
  1130. def note_refid(self, node):
  1131. self.refids.setdefault(node['refid'], []).append(node)
  1132. def note_indirect_target(self, target):
  1133. self.indirect_targets.append(target)
  1134. if target['names']:
  1135. self.note_refname(target)
  1136. def note_anonymous_target(self, target):
  1137. self.set_id(target)
  1138. def note_autofootnote(self, footnote):
  1139. self.set_id(footnote)
  1140. self.autofootnotes.append(footnote)
  1141. def note_autofootnote_ref(self, ref):
  1142. self.set_id(ref)
  1143. self.autofootnote_refs.append(ref)
  1144. def note_symbol_footnote(self, footnote):
  1145. self.set_id(footnote)
  1146. self.symbol_footnotes.append(footnote)
  1147. def note_symbol_footnote_ref(self, ref):
  1148. self.set_id(ref)
  1149. self.symbol_footnote_refs.append(ref)
  1150. def note_footnote(self, footnote):
  1151. self.set_id(footnote)
  1152. self.footnotes.append(footnote)
  1153. def note_footnote_ref(self, ref):
  1154. self.set_id(ref)
  1155. self.footnote_refs.setdefault(ref['refname'], []).append(ref)
  1156. self.note_refname(ref)
  1157. def note_citation(self, citation):
  1158. self.citations.append(citation)
  1159. def note_citation_ref(self, ref):
  1160. self.set_id(ref)
  1161. self.citation_refs.setdefault(ref['refname'], []).append(ref)
  1162. self.note_refname(ref)
  1163. def note_substitution_def(self, subdef, def_name, msgnode=None):
  1164. name = whitespace_normalize_name(def_name)
  1165. if name in self.substitution_defs:
  1166. msg = self.reporter.error(
  1167. 'Duplicate substitution definition name: "%s".' % name,
  1168. base_node=subdef)
  1169. if msgnode != None:
  1170. msgnode += msg
  1171. oldnode = self.substitution_defs[name]
  1172. dupname(oldnode, name)
  1173. # keep only the last definition:
  1174. self.substitution_defs[name] = subdef
  1175. # case-insensitive mapping:
  1176. self.substitution_names[fully_normalize_name(name)] = name
  1177. def note_substitution_ref(self, subref, refname):
  1178. subref['refname'] = whitespace_normalize_name(refname)
  1179. def note_pending(self, pending, priority=None):
  1180. self.transformer.add_pending(pending, priority)
  1181. def note_parse_message(self, message):
  1182. self.parse_messages.append(message)
  1183. def note_transform_message(self, message):
  1184. self.transform_messages.append(message)
  1185. def note_source(self, source, offset):
  1186. self.current_source = source
  1187. if offset is None:
  1188. self.current_line = offset
  1189. else:
  1190. self.current_line = offset + 1
  1191. def copy(self):
  1192. return self.__class__(self.settings, self.reporter,
  1193. **self.attributes)
  1194. def get_decoration(self):
  1195. if not self.decoration:
  1196. self.decoration = decoration()
  1197. index = self.first_child_not_matching_class(Titular)
  1198. if index is None:
  1199. self.append(self.decoration)
  1200. else:
  1201. self.insert(index, self.decoration)
  1202. return self.decoration
  1203. # ================
  1204. # Title Elements
  1205. # ================
  1206. class title(Titular, PreBibliographic, TextElement): pass
  1207. class subtitle(Titular, PreBibliographic, TextElement): pass
  1208. class rubric(Titular, TextElement): pass
  1209. # ========================
  1210. # Bibliographic Elements
  1211. # ========================
  1212. class docinfo(Bibliographic, Element): pass
  1213. class author(Bibliographic, TextElement): pass
  1214. class authors(Bibliographic, Element): pass
  1215. class organization(Bibliographic, TextElement): pass
  1216. class address(Bibliographic, FixedTextElement): pass
  1217. class contact(Bibliographic, TextElement): pass
  1218. class version(Bibliographic, TextElement): pass
  1219. class revision(Bibliographic, TextElement): pass
  1220. class status(Bibliographic, TextElement): pass
  1221. class date(Bibliographic, TextElement): pass
  1222. class copyright(Bibliographic, TextElement): pass
  1223. # =====================
  1224. # Decorative Elements
  1225. # =====================
  1226. class decoration(Decorative, Element):
  1227. def get_header(self):
  1228. if not len(self.children) or not isinstance(self.children[0], header):
  1229. self.insert(0, header())
  1230. return self.children[0]
  1231. def get_footer(self):
  1232. if not len(self.children) or not isinstance(self.children[-1], footer):
  1233. self.append(footer())
  1234. return self.children[-1]
  1235. class header(Decorative, Element): pass
  1236. class footer(Decorative, Element): pass
  1237. # =====================
  1238. # Structural Elements
  1239. # =====================
  1240. class section(Structural, Element): pass
  1241. class topic(Structural, Element):
  1242. """
  1243. Topics are terminal, "leaf" mini-sections, like block quotes with titles,
  1244. or textual figures. A topic is just like a section, except that it has no
  1245. subsections, and it doesn't have to conform to section placement rules.
  1246. Topics are allowed wherever body elements (list, table, etc.) are allowed,
  1247. but only at the top level of a section or document. Topics cannot nest
  1248. inside topics, sidebars, or body elements; you can't have a topic inside a
  1249. table, list, block quote, etc.
  1250. """
  1251. class sidebar(Structural, Element):
  1252. """
  1253. Sidebars are like miniature, parallel documents that occur inside other
  1254. documents, providing related or reference material. A sidebar is
  1255. typically offset by a border and "floats" to the side of the page; the
  1256. document's main text may flow around it. Sidebars can also be likened to
  1257. super-footnotes; their content is outside of the flow of the document's
  1258. main text.
  1259. Sidebars are allowed wherever body elements (list, table, etc.) are
  1260. allowed, but only at the top level of a section or document. Sidebars
  1261. cannot nest inside sidebars, topics, or body elements; you can't have a
  1262. sidebar inside a table, list, block quote, etc.
  1263. """
  1264. class transition(Structural, Element): pass
  1265. # ===============
  1266. # Body Elements
  1267. # ===============
  1268. class paragraph(General, TextElement): pass
  1269. class compound(General, Element): pass
  1270. class container(General, Element): pass
  1271. class bullet_list(Sequential, Element): pass
  1272. class enumerated_list(Sequential, Element): pass
  1273. class list_item(Part, Element): pass
  1274. class definition_list(Sequential, Element): pass
  1275. class definition_list_item(Part, Element): pass
  1276. class term(Part, TextElement): pass
  1277. class classifier(Part, TextElement): pass
  1278. class definition(Part, Element): pass
  1279. class field_list(Sequential, Element): pass
  1280. class field(Part, Element): pass
  1281. class field_name(Part, TextElement): pass
  1282. class field_body(Part, Element): pass
  1283. class option(Part, Element):
  1284. child_text_separator = ''
  1285. class option_argument(Part, TextElement):
  1286. def astext(self):
  1287. return self.get('delimiter', ' ') + TextElement.astext(self)
  1288. class option_group(Part, Element):
  1289. child_text_separator = ', '
  1290. class option_list(Sequential, Element): pass
  1291. class option_list_item(Part, Element):
  1292. child_text_separator = ' '
  1293. class option_string(Part, TextElement): pass
  1294. class description(Part, Element): pass
  1295. class literal_block(General, FixedTextElement): pass
  1296. class doctest_block(General, FixedTextElement): pass
  1297. class math_block(General, FixedTextElement): pass
  1298. class line_block(General, Element): pass
  1299. class line(Part, TextElement):
  1300. indent = None
  1301. class block_quote(General, Element): pass
  1302. class attribution(Part, TextElement): pass
  1303. class attention(Admonition, Element): pass
  1304. class caution(Admonition, Element): pass
  1305. class danger(Admonition, Element): pass
  1306. class error(Admonition, Element): pass
  1307. class important(Admonition, Element): pass
  1308. class note(Admonition, Element): pass
  1309. class tip(Admonition, Element): pass
  1310. class hint(Admonition, Element): pass
  1311. class warning(Admonition, Element): pass
  1312. class admonition(Admonition, Element): pass
  1313. class comment(Special, Invisible, FixedTextElement): pass
  1314. class substitution_definition(Special, Invisible, TextElement): pass
  1315. class target(Special, Invisible, Inline, TextElement, Targetable): pass
  1316. class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
  1317. class citation(General, BackLinkable, Element, Labeled, Targetable): pass
  1318. class label(Part, TextElement): pass
  1319. class figure(General, Element): pass
  1320. class caption(Part, TextElement): pass
  1321. class legend(Part, Element): pass
  1322. class table(General, Element): pass
  1323. class tgroup(Part, Element): pass
  1324. class colspec(Part, Element): pass
  1325. class thead(Part, Element): pass
  1326. class tbody(Part, Element): pass
  1327. class row(Part, Element): pass
  1328. class entry(Part, Element): pass
  1329. class system_message(Special, BackLinkable, PreBibliographic, Element):
  1330. """
  1331. System message element.
  1332. Do not instantiate this class directly; use
  1333. ``document.reporter.info/warning/error/severe()`` instead.
  1334. """
  1335. def __init__(self, message=None, *children, **attributes):
  1336. if message:
  1337. p = paragraph('', message)
  1338. children = (p,) + children
  1339. try:
  1340. Element.__init__(self, '', *children, **attributes)
  1341. except:
  1342. print('system_message: children=%r' % (children,))
  1343. raise
  1344. def astext(self):
  1345. line = self.get('line', '')
  1346. return '%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
  1347. self['level'], Element.astext(self))
  1348. class pending(Special, Invisible, Element):
  1349. """
  1350. The "pending" element is used to encapsulate a pending operation: the
  1351. operation (transform), the point at which to apply it, and any data it
  1352. requires. Only the pending operation's location within the document is
  1353. stored in the public document tree (by the "pending" object itself); the
  1354. operation and its data are stored in the "pending" object's internal
  1355. instance attributes.
  1356. For example, say you want a table of contents in your reStructuredText
  1357. document. The easiest way to specify where to put it is from within the
  1358. document, with a directive::
  1359. .. contents::
  1360. But the "contents" directive can't do its work until the entire document
  1361. has been parsed and possibly transformed to some extent. So the directive
  1362. code leaves a placeholder behind that will trigger the second phase of its
  1363. processing, something like this::
  1364. <pending ...public attributes...> + internal attributes
  1365. Use `document.note_pending()` so that the
  1366. `docutils.transforms.Transformer` stage of processing can run all pending
  1367. transforms.
  1368. """
  1369. def __init__(self, transform, details=None,
  1370. rawsource='', *children, **attributes):
  1371. Element.__init__(self, rawsource, *children, **attributes)
  1372. self.transform = transform
  1373. """The `docutils.transforms.Transform` class implementing the pending
  1374. operation."""
  1375. self.details = details or {}
  1376. """Detail data (dictionary) required by the pending operation."""
  1377. def pformat(self, indent=' ', level=0):
  1378. internals = [
  1379. '.. internal attributes:',
  1380. ' .transform: %s.%s' % (self.transform.__module__,
  1381. self.transform.__name__),
  1382. ' .details:']
  1383. details = list(self.details.items())
  1384. details.sort()
  1385. for key, value in details:
  1386. if isinstance(value, Node):
  1387. internals.append('%7s%s:' % ('', key))
  1388. internals.extend(['%9s%s' % ('', line)
  1389. for line in value.pformat().splitlines()])
  1390. elif value and isinstance(value, list) \
  1391. and isinstance(value[0], Node):
  1392. internals.append('%7s%s:' % ('', key))
  1393. for v in value:
  1394. internals.extend(['%9s%s' % ('', line)
  1395. for line in v.pformat().splitlines()])
  1396. else:
  1397. internals.append('%7s%s: %r' % ('', key, value))
  1398. return (Element.pformat(self, indent, level)
  1399. + ''.join([(' %s%s\n' % (indent * level, line))
  1400. for line in internals]))
  1401. def copy(self):
  1402. return self.__class__(self.transform, self.details, self.rawsource,
  1403. **self.attributes)
  1404. class raw(Special, Inline, PreBibliographic, FixedTextElement):
  1405. """
  1406. Raw data that is to be passed untouched to the Writer.
  1407. """
  1408. pass
  1409. # =================
  1410. # Inline Elements
  1411. # =================
  1412. class emphasis(Inline, TextElement): pass
  1413. class strong(Inline, TextElement): pass
  1414. class literal(Inline, TextElement): pass
  1415. class reference(General, Inline, Referential, TextElement): pass
  1416. class footnote_reference(Inline, Referential, TextElement): pass
  1417. class citation_reference(Inline, Referential, TextElement): pass
  1418. class substitution_reference(Inline, TextElement): pass
  1419. class title_reference(Inline, TextElement): pass
  1420. class abbreviation(Inline, TextElement): pass
  1421. class acronym(Inline, TextElement): pass
  1422. class superscript(Inline, TextElement): pass
  1423. class subscript(Inline, TextElement): pass
  1424. class math(Inline, TextElement): pass
  1425. class image(General, Inline, Element):
  1426. def astext(self):
  1427. return self.get('alt', '')
  1428. class inline(Inline, TextElement): pass
  1429. class problematic(Inline, TextElement): pass
  1430. class generated(Inline, TextElement): pass
  1431. # ========================================
  1432. # Auxiliary Classes, Functions, and Data
  1433. # ========================================
  1434. node_class_names = """
  1435. Text
  1436. abbreviation acronym address admonition attention attribution author
  1437. authors
  1438. block_quote bullet_list
  1439. caption caution citation citation_reference classifier colspec comment
  1440. compound contact container copyright
  1441. danger date decoration definition definition_list definition_list_item
  1442. description docinfo doctest_block document
  1443. emphasis entry enumerated_list error
  1444. field field_body field_list field_name figure footer
  1445. footnote footnote_reference
  1446. generated
  1447. header hint
  1448. image important inline
  1449. label legend line line_block list_item literal literal_block
  1450. math math_block
  1451. note
  1452. option option_argument option_group option_list option_list_item
  1453. option_string organization
  1454. paragraph pending problematic
  1455. raw reference revision row rubric
  1456. section sidebar status strong subscript substitution_definition
  1457. substitution_reference subtitle superscript system_message
  1458. table target tbody term tgroup thead tip title title_reference topic
  1459. transition
  1460. version
  1461. warning""".split()
  1462. """A list of names of all concrete Node subclasses."""
  1463. class NodeVisitor:
  1464. """
  1465. "Visitor" pattern [GoF95]_ abstract superclass implementation for
  1466. document tree traversals.
  1467. Each node class has corresponding methods, doing nothing by
  1468. default; override individual methods for specific and useful
  1469. behaviour. The `dispatch_visit()` method is called by
  1470. `Node.walk()` upon entering a node. `Node.walkabout()` also calls
  1471. the `dispatch_departure()` method before exiting a node.
  1472. The dispatch methods call "``visit_`` + node class name" or
  1473. "``depart_`` + node class name", resp.
  1474. This is a base class for visitors whose ``visit_...`` & ``depart_...``
  1475. methods should be implemented for *all* node types encountered (such as
  1476. for `docutils.writers.Writer` subclasses). Unimplemented methods will
  1477. raise exceptions.
  1478. For sparse traversals, where only certain node types are of interest,
  1479. subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform
  1480. processing is desired, subclass `GenericNodeVisitor`.
  1481. .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
  1482. Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
  1483. 1995.
  1484. """
  1485. optional = ()
  1486. """
  1487. Tuple containing node class names (as strings).
  1488. No exception will be raised if writers do not implement visit
  1489. or departure functions for these node classes.
  1490. Used to ensure transitional compatibility with existing 3rd-party writers.
  1491. """
  1492. def __init__(self, document):
  1493. self.document = document
  1494. def dispatch_visit(self, node):
  1495. """
  1496. Call self."``visit_`` + node class name" with `node` as
  1497. parameter. If the ``visit_...`` method does not exist, call
  1498. self.unknown_visit.
  1499. """
  1500. node_name = node.__class__.__name__
  1501. method = getattr(self, 'visit_' + node_name, self.unknown_visit)
  1502. self.document.reporter.debug(
  1503. 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
  1504. % (method.__name__, node_name))
  1505. return method(node)
  1506. def dispatch_departure(self, node):
  1507. """
  1508. Call self."``depart_`` + node class name" with `node` as
  1509. parameter. If the ``depart_...`` method does not exist, call
  1510. self.unknown_departure.
  1511. """
  1512. node_name = node.__class__.__name__
  1513. method = getattr(self, 'depart_' + node_name, self.unknown_departure)
  1514. self.document.reporter.debug(
  1515. 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
  1516. % (method.__name__, node_name))
  1517. return method(node)
  1518. def unknown_visit(self, node):
  1519. """
  1520. Called when entering unknown `Node` types.
  1521. Raise an exception unless overridden.
  1522. """
  1523. if (self.document.settings.strict_visitor
  1524. or node.__class__.__name__ not in self.optional):
  1525. raise NotImplementedError(
  1526. '%s visiting unknown node type: %s'
  1527. % (self.__class__, node.__class__.__name__))
  1528. def unknown_departure(self, node):
  1529. """
  1530. Called before exiting unknown `Node` types.
  1531. Raise exception unless overridden.
  1532. """
  1533. if (self.document.settings.strict_visitor
  1534. or node.__class__.__name__ not in self.optional):
  1535. raise NotImplementedError(
  1536. '%s departing unknown node type: %s'
  1537. % (self.__class__, node.__class__.__name__))
  1538. class SparseNodeVisitor(NodeVisitor):
  1539. """
  1540. Base class for sparse traversals, where only certain node types are of
  1541. interest. When ``visit_...`` & ``depart_...`` methods should be
  1542. implemented for *all* node types (such as for `docutils.writers.Writer`
  1543. subclasses), subclass `NodeVisitor` instead.
  1544. """
  1545. class GenericNodeVisitor(NodeVisitor):
  1546. """
  1547. Generic "Visitor" abstract superclass, for simple traversals.
  1548. Unless overridden, each ``visit_...`` method calls `default_visit()`, and
  1549. each ``depart_...`` method (when using `Node.walkabout()`) calls
  1550. `default_departure()`. `default_visit()` (and `default_departure()`) must
  1551. be overridden in subclasses.
  1552. Define fully generic visitors by overriding `default_visit()` (and
  1553. `default_departure()`) only. Define semi-generic visitors by overriding
  1554. individual ``visit_...()`` (and ``depart_...()``) methods also.
  1555. `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should
  1556. be overridden for default behavior.
  1557. """
  1558. def default_visit(self, node):
  1559. """Override for generic, uniform traversals."""
  1560. raise NotImplementedError
  1561. def default_departure(self, node):
  1562. """Override for generic, uniform traversals."""
  1563. raise NotImplementedError
  1564. def _call_default_visit(self, node):
  1565. self.default_visit(node)
  1566. def _call_default_departure(self, node):
  1567. self.default_departure(node)
  1568. def _nop(self, node):
  1569. pass
  1570. def _add_node_class_names(names):
  1571. """Save typing with dynamic assignments:"""
  1572. for _name in names:
  1573. setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
  1574. setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
  1575. setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
  1576. setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
  1577. _add_node_class_names(node_class_names)
  1578. class TreeCopyVisitor(GenericNodeVisitor):
  1579. """
  1580. Make a complete copy of a tree or branch, including element attributes.
  1581. """
  1582. def __init__(self, document):
  1583. GenericNodeVisitor.__init__(self, document)
  1584. self.parent_stack = []
  1585. self.parent = []
  1586. def get_tree_copy(self):
  1587. return self.parent[0]
  1588. def default_visit(self, node):
  1589. """Copy the current node, and make it the new acting parent."""
  1590. newnode = node.copy()
  1591. self.parent.append(newnode)
  1592. self.parent_stack.append(self.parent)
  1593. self.parent = newnode
  1594. def default_departure(self, node):
  1595. """Restore the previous acting parent."""
  1596. self.parent = self.parent_stack.pop()
  1597. class TreePruningException(Exception):
  1598. """
  1599. Base class for `NodeVisitor`-related tree pruning exceptions.
  1600. Raise subclasses from within ``visit_...`` or ``depart_...`` methods
  1601. called from `Node.walk()` and `Node.walkabout()` tree traversals to prune
  1602. the tree traversed.
  1603. """
  1604. pass
  1605. class SkipChildren(TreePruningException):
  1606. """
  1607. Do not visit any children of the current node. The current node's
  1608. siblings and ``depart_...`` method are not affected.
  1609. """
  1610. pass
  1611. class SkipSiblings(TreePruningException):
  1612. """
  1613. Do not visit any more siblings (to the right) of the current node. The
  1614. current node's children and its ``depart_...`` method are not affected.
  1615. """
  1616. pass
  1617. class SkipNode(TreePruningException):
  1618. """
  1619. Do not visit the current node's children, and do not call the current
  1620. node's ``depart_...`` method.
  1621. """
  1622. pass
  1623. class SkipDeparture(TreePruningException):
  1624. """
  1625. Do not call the current node's ``depart_...`` method. The current node's
  1626. children and siblings are not affected.
  1627. """
  1628. pass
  1629. class NodeFound(TreePruningException):
  1630. """
  1631. Raise to indicate that the target of a search has been found. This
  1632. exception must be caught by the client; it is not caught by the traversal
  1633. code.
  1634. """
  1635. pass
  1636. class StopTraversal(TreePruningException):
  1637. """
  1638. Stop the traversal alltogether. The current node's ``depart_...`` method
  1639. is not affected. The parent nodes ``depart_...`` methods are also called
  1640. as usual. No other nodes are visited. This is an alternative to
  1641. NodeFound that does not cause exception handling to trickle up to the
  1642. caller.
  1643. """
  1644. pass
  1645. def make_id(string):
  1646. """
  1647. Convert `string` into an identifier and return it.
  1648. Docutils identifiers will conform to the regular expression
  1649. ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class"
  1650. and "id" attributes) should have no underscores, colons, or periods.
  1651. Hyphens may be used.
  1652. - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
  1653. ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
  1654. followed by any number of letters, digits ([0-9]), hyphens ("-"),
  1655. underscores ("_"), colons (":"), and periods (".").
  1656. - However the `CSS1 spec`_ defines identifiers based on the "name" token,
  1657. a tighter interpretation ("flex" tokenizer notation; "latin1" and
  1658. "escape" 8-bit characters have been replaced with entities)::
  1659. unicode \\[0-9a-f]{1,4}
  1660. latin1 [&iexcl;-&yuml;]
  1661. escape {unicode}|\\[ -~&iexcl;-&yuml;]
  1662. nmchar [-a-z0-9]|{latin1}|{escape}
  1663. name {nmchar}+
  1664. The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"),
  1665. or periods ("."), therefore "class" and "id" attributes should not contain
  1666. these characters. They should be replaced with hyphens ("-"). Combined
  1667. with HTML's requirements (the first character must be a letter; no
  1668. "unicode", "latin1", or "escape" characters), this results in the
  1669. ``[a-z](-?[a-z0-9]+)*`` pattern.
  1670. .. _HTML 4.01 spec: http://www.w3.org/TR/html401
  1671. .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
  1672. """
  1673. id = string.lower()
  1674. if not isinstance(id, str):
  1675. id = id.decode()
  1676. id = id.translate(_non_id_translate_digraphs)
  1677. id = id.translate(_non_id_translate)
  1678. # get rid of non-ascii characters.
  1679. # 'ascii' lowercase to prevent problems with turkish locale.
  1680. id = unicodedata.normalize('NFKD', id).\
  1681. encode('ascii', 'ignore').decode('ascii')
  1682. # shrink runs of whitespace and replace by hyphen
  1683. id = _non_id_chars.sub('-', ' '.join(id.split()))
  1684. id = _non_id_at_ends.sub('', id)
  1685. return str(id)
  1686. _non_id_chars = re.compile('[^a-z0-9]+')
  1687. _non_id_at_ends = re.compile('^[-0-9]+|-+$')
  1688. _non_id_translate = {
  1689. 0x00f8: 'o', # o with stroke
  1690. 0x0111: 'd', # d with stroke
  1691. 0x0127: 'h', # h with stroke
  1692. 0x0131: 'i', # dotless i
  1693. 0x0142: 'l', # l with stroke
  1694. 0x0167: 't', # t with stroke
  1695. 0x0180: 'b', # b with stroke
  1696. 0x0183: 'b', # b with topbar
  1697. 0x0188: 'c', # c with hook
  1698. 0x018c: 'd', # d with topbar
  1699. 0x0192: 'f', # f with hook
  1700. 0x0199: 'k', # k with hook
  1701. 0x019a: 'l', # l with bar
  1702. 0x019e: 'n', # n with long right leg
  1703. 0x01a5: 'p', # p with hook
  1704. 0x01ab: 't', # t with palatal hook
  1705. 0x01ad: 't', # t with hook
  1706. 0x01b4: 'y', # y with hook
  1707. 0x01b6: 'z', # z with stroke
  1708. 0x01e5: 'g', # g with stroke
  1709. 0x0225: 'z', # z with hook
  1710. 0x0234: 'l', # l with curl
  1711. 0x0235: 'n', # n with curl
  1712. 0x0236: 't', # t with curl
  1713. 0x0237: 'j', # dotless j
  1714. 0x023c: 'c', # c with stroke
  1715. 0x023f: 's', # s with swash tail
  1716. 0x0240: 'z', # z with swash tail
  1717. 0x0247: 'e', # e with stroke
  1718. 0x0249: 'j', # j with stroke
  1719. 0x024b: 'q', # q with hook tail
  1720. 0x024d: 'r', # r with stroke
  1721. 0x024f: 'y', # y with stroke
  1722. }
  1723. _non_id_translate_digraphs = {
  1724. 0x00df: 'sz', # ligature sz
  1725. 0x00e6: 'ae', # ae
  1726. 0x0153: 'oe', # ligature oe
  1727. 0x0238: 'db', # db digraph
  1728. 0x0239: 'qp', # qp digraph
  1729. }
  1730. def dupname(node, name):
  1731. node['dupnames'].append(name)
  1732. node['names'].remove(name)
  1733. # Assume that this method is referenced, even though it isn't; we
  1734. # don't want to throw unnecessary system_messages.
  1735. node.referenced = 1
  1736. def fully_normalize_name(name):
  1737. """Return a case- and whitespace-normalized name."""
  1738. return ' '.join(name.lower().split())
  1739. def whitespace_normalize_name(name):
  1740. """Return a whitespace-normalized name."""
  1741. return ' '.join(name.split())
  1742. def serial_escape(value):
  1743. """Escape string values that are elements of a list, for serialization."""
  1744. return value.replace('\\', r'\\').replace(' ', r'\ ')
  1745. def pseudo_quoteattr(value):
  1746. """Quote attributes for pseudo-xml"""
  1747. return '"%s"' % value
  1748. #
  1749. #
  1750. # Local Variables:
  1751. # indent-tabs-mode: nil
  1752. # sentence-end-double-space: t
  1753. # fill-column: 78
  1754. # End: