PageRenderTime 29ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/External.LCA_RESTRICTED/Languages/IronPython/27/Doc/docutils/nodes.py

http://github.com/IronLanguages/main
Python | 1801 lines | 1729 code | 19 blank | 53 comment | 21 complexity | f13a6391190ee7d2d85624cb3e126832 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. # $Id: nodes.py 5033 2007-03-21 19:51:22Z wiemann $
  2. # Author: David Goodger <goodger@python.org>
  3. # Copyright: This module has been placed in the public domain.
  4. """
  5. Docutils document tree element class library.
  6. Classes in CamelCase are abstract base classes or auxiliary classes. The one
  7. exception is `Text`, for a text (PCDATA) node; uppercase is used to
  8. differentiate from element classes. Classes in lower_case_with_underscores
  9. are element classes, matching the XML element generic identifiers in the DTD_.
  10. The position of each node (the level at which it can occur) is significant and
  11. is represented by abstract base classes (`Root`, `Structural`, `Body`,
  12. `Inline`, etc.). Certain transformations will be easier because we can use
  13. ``isinstance(node, base_class)`` to determine the position of the node in the
  14. hierarchy.
  15. .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
  16. """
  17. __docformat__ = 'reStructuredText'
  18. import sys
  19. import os
  20. import re
  21. import warnings
  22. from types import IntType, SliceType, StringType, UnicodeType, \
  23. TupleType, ListType, ClassType, TypeType
  24. from UserString import UserString
  25. # ==============================
  26. # Functional Node Base Classes
  27. # ==============================
  28. class Node:
  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 __nonzero__(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 1
  47. def __str__(self):
  48. return self.__unicode__().encode('raw_unicode_escape')
  49. def __unicode__(self):
  50. # Override in subclass.
  51. raise NotImplementedError
  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 = 0
  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 = 1
  113. break
  114. except SkipSiblings:
  115. pass
  116. except StopTraversal:
  117. stop = 1
  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 = 1
  130. stop = 0
  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 = 0
  141. children = self.children
  142. try:
  143. for child in children[:]:
  144. if child.walkabout(visitor):
  145. stop = 1
  146. break
  147. except SkipSiblings:
  148. pass
  149. except SkipChildren:
  150. pass
  151. except StopTraversal:
  152. stop = 1
  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 traverse(self, condition=None,
  160. include_self=1, descend=1, siblings=0, ascend=0):
  161. """
  162. Return an iterable containing
  163. * self (if include_self is true)
  164. * all descendants in tree traversal order (if descend is true)
  165. * all siblings (if siblings is true) and their descendants (if
  166. also descend is true)
  167. * the siblings of the parent (if ascend is true) and their
  168. descendants (if also descend is true), and so on
  169. If `condition` is not None, the iterable contains only nodes
  170. for which ``condition(node)`` is true. If `condition` is a
  171. node class ``cls``, it is equivalent to a function consisting
  172. of ``return isinstance(node, cls)``.
  173. If ascend is true, assume siblings to be true as well.
  174. For example, given the following tree::
  175. <paragraph>
  176. <emphasis> <--- emphasis.traverse() and
  177. <strong> <--- strong.traverse() are called.
  178. Foo
  179. Bar
  180. <reference name="Baz" refid="baz">
  181. Baz
  182. Then list(emphasis.traverse()) equals ::
  183. [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
  184. and list(strong.traverse(ascend=1)) equals ::
  185. [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
  186. """
  187. r = []
  188. if ascend:
  189. siblings=1
  190. # Check if `condition` is a class (check for TypeType for Python
  191. # implementations that use only new-style classes, like PyPy).
  192. if isinstance(condition, (ClassType, TypeType)):
  193. node_class = condition
  194. def condition(node, node_class=node_class):
  195. return isinstance(node, node_class)
  196. if include_self and (condition is None or condition(self)):
  197. r.append(self)
  198. if descend and len(self.children):
  199. for child in self:
  200. r.extend(child.traverse(
  201. include_self=1, descend=1, siblings=0, ascend=0,
  202. condition=condition))
  203. if siblings or ascend:
  204. node = self
  205. while node.parent:
  206. index = node.parent.index(node)
  207. for sibling in node.parent[index+1:]:
  208. r.extend(sibling.traverse(include_self=1, descend=descend,
  209. siblings=0, ascend=0,
  210. condition=condition))
  211. if not ascend:
  212. break
  213. else:
  214. node = node.parent
  215. return r
  216. def next_node(self, condition=None,
  217. include_self=0, descend=1, siblings=0, ascend=0):
  218. """
  219. Return the first node in the iterable returned by traverse(),
  220. or None if the iterable is empty.
  221. Parameter list is the same as of traverse. Note that
  222. include_self defaults to 0, though.
  223. """
  224. iterable = self.traverse(condition=condition,
  225. include_self=include_self, descend=descend,
  226. siblings=siblings, ascend=ascend)
  227. try:
  228. return iterable[0]
  229. except IndexError:
  230. return None
  231. class Text(Node, UserString):
  232. """
  233. Instances are terminal nodes (leaves) containing text only; no child
  234. nodes or attributes. Initialize by passing a string to the constructor.
  235. Access the text itself with the `astext` method.
  236. """
  237. tagname = '#text'
  238. children = ()
  239. """Text nodes have no children, and cannot have children."""
  240. def __init__(self, data, rawsource=''):
  241. UserString.__init__(self, data)
  242. self.rawsource = rawsource
  243. """The raw text from which this element was constructed."""
  244. def __repr__(self):
  245. data = repr(self.data)
  246. if len(data) > 70:
  247. data = repr(self.data[:64] + ' ...')
  248. return '<%s: %s>' % (self.tagname, data)
  249. def __len__(self):
  250. return len(self.data)
  251. def shortrepr(self):
  252. data = repr(self.data)
  253. if len(data) > 20:
  254. data = repr(self.data[:16] + ' ...')
  255. return '<%s: %s>' % (self.tagname, data)
  256. def _dom_node(self, domroot):
  257. return domroot.createTextNode(self.data)
  258. def astext(self):
  259. return self.data
  260. def __unicode__(self):
  261. return self.data
  262. def copy(self):
  263. return self.__class__(self.data)
  264. def deepcopy(self):
  265. return self.copy()
  266. def pformat(self, indent=' ', level=0):
  267. result = []
  268. indent = indent * level
  269. for line in self.data.splitlines():
  270. result.append(indent + line + '\n')
  271. return ''.join(result)
  272. class Element(Node):
  273. """
  274. `Element` is the superclass to all specific elements.
  275. Elements contain attributes and child nodes. Elements emulate
  276. dictionaries for attributes, indexing by attribute name (a string). To
  277. set the attribute 'att' to 'value', do::
  278. element['att'] = 'value'
  279. There are two special attributes: 'ids' and 'names'. Both are
  280. lists of unique identifiers, and names serve as human interfaces
  281. to IDs. Names are case- and whitespace-normalized (see the
  282. fully_normalize_name() function), and IDs conform to the regular
  283. expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
  284. Elements also emulate lists for child nodes (element nodes and/or text
  285. nodes), indexing by integer. To get the first child node, use::
  286. element[0]
  287. Elements may be constructed using the ``+=`` operator. To add one new
  288. child node to element, do::
  289. element += node
  290. This is equivalent to ``element.append(node)``.
  291. To add a list of multiple child nodes at once, use the same ``+=``
  292. operator::
  293. element += [node1, node2]
  294. This is equivalent to ``element.extend([node1, node2])``.
  295. """
  296. list_attributes = ('ids', 'classes', 'names', 'dupnames', 'backrefs')
  297. """List attributes, automatically initialized to empty lists for
  298. all nodes."""
  299. tagname = None
  300. """The element generic identifier. If None, it is set as an instance
  301. attribute to the name of the class."""
  302. child_text_separator = '\n\n'
  303. """Separator for child nodes, used by `astext()` method."""
  304. def __init__(self, rawsource='', *children, **attributes):
  305. self.rawsource = rawsource
  306. """The raw text from which this element was constructed."""
  307. self.children = []
  308. """List of child nodes (elements and/or `Text`)."""
  309. self.extend(children) # maintain parent info
  310. self.attributes = {}
  311. """Dictionary of attribute {name: value}."""
  312. # Initialize list attributes.
  313. for att in self.list_attributes:
  314. self.attributes[att] = []
  315. for att, value in attributes.items():
  316. att = att.lower()
  317. if att in self.list_attributes:
  318. # mutable list; make a copy for this node
  319. self.attributes[att] = value[:]
  320. else:
  321. self.attributes[att] = value
  322. if self.tagname is None:
  323. self.tagname = self.__class__.__name__
  324. def _dom_node(self, domroot):
  325. element = domroot.createElement(self.tagname)
  326. for attribute, value in self.attlist():
  327. if isinstance(value, ListType):
  328. value = ' '.join([serial_escape('%s' % v) for v in value])
  329. element.setAttribute(attribute, '%s' % value)
  330. for child in self.children:
  331. element.appendChild(child._dom_node(domroot))
  332. return element
  333. def __repr__(self):
  334. data = ''
  335. for c in self.children:
  336. data += c.shortrepr()
  337. if len(data) > 60:
  338. data = data[:56] + ' ...'
  339. break
  340. if self['names']:
  341. return '<%s "%s": %s>' % (self.__class__.__name__,
  342. '; '.join(self['names']), data)
  343. else:
  344. return '<%s: %s>' % (self.__class__.__name__, data)
  345. def shortrepr(self):
  346. if self['names']:
  347. return '<%s "%s"...>' % (self.__class__.__name__,
  348. '; '.join(self['names']))
  349. else:
  350. return '<%s...>' % self.tagname
  351. def __unicode__(self):
  352. if self.children:
  353. return u'%s%s%s' % (self.starttag(),
  354. ''.join([unicode(c) for c in self.children]),
  355. self.endtag())
  356. else:
  357. return self.emptytag()
  358. def starttag(self):
  359. parts = [self.tagname]
  360. for name, value in self.attlist():
  361. if value is None: # boolean attribute
  362. parts.append(name)
  363. elif isinstance(value, ListType):
  364. values = [serial_escape('%s' % v) for v in value]
  365. parts.append('%s="%s"' % (name, ' '.join(values)))
  366. else:
  367. parts.append('%s="%s"' % (name, value))
  368. return '<%s>' % ' '.join(parts)
  369. def endtag(self):
  370. return '</%s>' % self.tagname
  371. def emptytag(self):
  372. return u'<%s/>' % ' '.join([self.tagname] +
  373. ['%s="%s"' % (n, v)
  374. for n, v in self.attlist()])
  375. def __len__(self):
  376. return len(self.children)
  377. def __getitem__(self, key):
  378. if isinstance(key, UnicodeType) or isinstance(key, StringType):
  379. return self.attributes[key]
  380. elif isinstance(key, IntType):
  381. return self.children[key]
  382. elif isinstance(key, SliceType):
  383. assert key.step in (None, 1), 'cannot handle slice with stride'
  384. return self.children[key.start:key.stop]
  385. else:
  386. raise TypeError, ('element index must be an integer, a slice, or '
  387. 'an attribute name string')
  388. def __setitem__(self, key, item):
  389. if isinstance(key, UnicodeType) or isinstance(key, StringType):
  390. self.attributes[str(key)] = item
  391. elif isinstance(key, IntType):
  392. self.setup_child(item)
  393. self.children[key] = item
  394. elif isinstance(key, SliceType):
  395. assert key.step in (None, 1), 'cannot handle slice with stride'
  396. for node in item:
  397. self.setup_child(node)
  398. self.children[key.start:key.stop] = item
  399. else:
  400. raise TypeError, ('element index must be an integer, a slice, or '
  401. 'an attribute name string')
  402. def __delitem__(self, key):
  403. if isinstance(key, UnicodeType) or isinstance(key, StringType):
  404. del self.attributes[key]
  405. elif isinstance(key, IntType):
  406. del self.children[key]
  407. elif isinstance(key, SliceType):
  408. assert key.step in (None, 1), 'cannot handle slice with stride'
  409. del self.children[key.start:key.stop]
  410. else:
  411. raise TypeError, ('element index must be an integer, a simple '
  412. 'slice, or an attribute name string')
  413. def __add__(self, other):
  414. return self.children + other
  415. def __radd__(self, other):
  416. return other + self.children
  417. def __iadd__(self, other):
  418. """Append a node or a list of nodes to `self.children`."""
  419. if isinstance(other, Node):
  420. self.append(other)
  421. elif other is not None:
  422. self.extend(other)
  423. return self
  424. def astext(self):
  425. return self.child_text_separator.join(
  426. [child.astext() for child in self.children])
  427. def non_default_attributes(self):
  428. atts = {}
  429. for key, value in self.attributes.items():
  430. if self.is_not_default(key):
  431. atts[key] = value
  432. return atts
  433. def attlist(self):
  434. attlist = self.non_default_attributes().items()
  435. attlist.sort()
  436. return attlist
  437. def get(self, key, failobj=None):
  438. return self.attributes.get(key, failobj)
  439. def hasattr(self, attr):
  440. return self.attributes.has_key(attr)
  441. def delattr(self, attr):
  442. if self.attributes.has_key(attr):
  443. del self.attributes[attr]
  444. def setdefault(self, key, failobj=None):
  445. return self.attributes.setdefault(key, failobj)
  446. has_key = hasattr
  447. def append(self, item):
  448. self.setup_child(item)
  449. self.children.append(item)
  450. def extend(self, item):
  451. for node in item:
  452. self.append(node)
  453. def insert(self, index, item):
  454. if isinstance(item, Node):
  455. self.setup_child(item)
  456. self.children.insert(index, item)
  457. elif item is not None:
  458. self[index:index] = item
  459. def pop(self, i=-1):
  460. return self.children.pop(i)
  461. def remove(self, item):
  462. self.children.remove(item)
  463. def index(self, item):
  464. return self.children.index(item)
  465. def is_not_default(self, key):
  466. if self[key] == [] and key in self.list_attributes:
  467. return 0
  468. else:
  469. return 1
  470. def update_basic_atts(self, dict):
  471. """
  472. Update basic attributes ('ids', 'names', 'classes',
  473. 'dupnames', but not 'source') from node or dictionary `dict`.
  474. """
  475. if isinstance(dict, Node):
  476. dict = dict.attributes
  477. for att in ('ids', 'classes', 'names', 'dupnames'):
  478. for value in dict.get(att, []):
  479. if not value in self[att]:
  480. self[att].append(value)
  481. def clear(self):
  482. self.children = []
  483. def replace(self, old, new):
  484. """Replace one child `Node` with another child or children."""
  485. index = self.index(old)
  486. if isinstance(new, Node):
  487. self.setup_child(new)
  488. self[index] = new
  489. elif new is not None:
  490. self[index:index+1] = new
  491. def replace_self(self, new):
  492. """
  493. Replace `self` node with `new`, where `new` is a node or a
  494. list of nodes.
  495. """
  496. update = new
  497. if not isinstance(new, Node):
  498. # `new` is a list; update first child.
  499. try:
  500. update = new[0]
  501. except IndexError:
  502. update = None
  503. if isinstance(update, Element):
  504. update.update_basic_atts(self)
  505. else:
  506. # `update` is a Text node or `new` is an empty list.
  507. # Assert that we aren't losing any attributes.
  508. for att in ('ids', 'names', 'classes', 'dupnames'):
  509. assert not self[att], \
  510. 'Losing "%s" attribute: %s' % (att, self[att])
  511. self.parent.replace(self, new)
  512. def first_child_matching_class(self, childclass, start=0, end=sys.maxint):
  513. """
  514. Return the index of the first child whose class exactly matches.
  515. Parameters:
  516. - `childclass`: A `Node` subclass to search for, or a tuple of `Node`
  517. classes. If a tuple, any of the classes may match.
  518. - `start`: Initial index to check.
  519. - `end`: Initial index to *not* check.
  520. """
  521. if not isinstance(childclass, TupleType):
  522. childclass = (childclass,)
  523. for index in range(start, min(len(self), end)):
  524. for c in childclass:
  525. if isinstance(self[index], c):
  526. return index
  527. return None
  528. def first_child_not_matching_class(self, childclass, start=0,
  529. end=sys.maxint):
  530. """
  531. Return the index of the first child whose class does *not* match.
  532. Parameters:
  533. - `childclass`: A `Node` subclass to skip, or a tuple of `Node`
  534. classes. If a tuple, none of the classes may match.
  535. - `start`: Initial index to check.
  536. - `end`: Initial index to *not* check.
  537. """
  538. if not isinstance(childclass, TupleType):
  539. childclass = (childclass,)
  540. for index in range(start, min(len(self), end)):
  541. for c in childclass:
  542. if isinstance(self.children[index], c):
  543. break
  544. else:
  545. return index
  546. return None
  547. def pformat(self, indent=' ', level=0):
  548. return ''.join(['%s%s\n' % (indent * level, self.starttag())] +
  549. [child.pformat(indent, level+1)
  550. for child in self.children])
  551. def copy(self):
  552. return self.__class__(**self.attributes)
  553. def deepcopy(self):
  554. copy = self.copy()
  555. copy.extend([child.deepcopy() for child in self.children])
  556. return copy
  557. def set_class(self, name):
  558. """Add a new class to the "classes" attribute."""
  559. warnings.warn('docutils.nodes.Element.set_class deprecated; '
  560. "append to Element['classes'] list attribute directly",
  561. DeprecationWarning, stacklevel=2)
  562. assert ' ' not in name
  563. self['classes'].append(name.lower())
  564. def note_referenced_by(self, name=None, id=None):
  565. """Note that this Element has been referenced by its name
  566. `name` or id `id`."""
  567. self.referenced = 1
  568. # Element.expect_referenced_by_* dictionaries map names or ids
  569. # to nodes whose ``referenced`` attribute is set to true as
  570. # soon as this node is referenced by the given name or id.
  571. # Needed for target propagation.
  572. by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
  573. by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
  574. if by_name:
  575. assert name is not None
  576. by_name.referenced = 1
  577. if by_id:
  578. assert id is not None
  579. by_id.referenced = 1
  580. class TextElement(Element):
  581. """
  582. An element which directly contains text.
  583. Its children are all `Text` or `Inline` subclass nodes. You can
  584. check whether an element's context is inline simply by checking whether
  585. its immediate parent is a `TextElement` instance (including subclasses).
  586. This is handy for nodes like `image` that can appear both inline and as
  587. standalone body elements.
  588. If passing children to `__init__()`, make sure to set `text` to
  589. ``''`` or some other suitable value.
  590. """
  591. child_text_separator = ''
  592. """Separator for child nodes, used by `astext()` method."""
  593. def __init__(self, rawsource='', text='', *children, **attributes):
  594. if text != '':
  595. textnode = Text(text)
  596. Element.__init__(self, rawsource, textnode, *children,
  597. **attributes)
  598. else:
  599. Element.__init__(self, rawsource, *children, **attributes)
  600. class FixedTextElement(TextElement):
  601. """An element which directly contains preformatted text."""
  602. def __init__(self, rawsource='', text='', *children, **attributes):
  603. TextElement.__init__(self, rawsource, text, *children, **attributes)
  604. self.attributes['xml:space'] = 'preserve'
  605. # ========
  606. # Mixins
  607. # ========
  608. class Resolvable:
  609. resolved = 0
  610. class BackLinkable:
  611. def add_backref(self, refid):
  612. self['backrefs'].append(refid)
  613. # ====================
  614. # Element Categories
  615. # ====================
  616. class Root: pass
  617. class Titular: pass
  618. class PreBibliographic:
  619. """Category of Node which may occur before Bibliographic Nodes."""
  620. class Bibliographic: pass
  621. class Decorative(PreBibliographic): pass
  622. class Structural: pass
  623. class Body: pass
  624. class General(Body): pass
  625. class Sequential(Body):
  626. """List-like elements."""
  627. class Admonition(Body): pass
  628. class Special(Body):
  629. """Special internal body elements."""
  630. class Invisible(PreBibliographic):
  631. """Internal elements that don't appear in output."""
  632. class Part: pass
  633. class Inline: pass
  634. class Referential(Resolvable): pass
  635. class Targetable(Resolvable):
  636. referenced = 0
  637. indirect_reference_name = None
  638. """Holds the whitespace_normalized_name (contains mixed case) of a target.
  639. Required for MoinMoin/reST compatibility."""
  640. class Labeled:
  641. """Contains a `label` as its first element."""
  642. # ==============
  643. # Root Element
  644. # ==============
  645. class document(Root, Structural, Element):
  646. """
  647. The document root element.
  648. Do not instantiate this class directly; use
  649. `docutils.utils.new_document()` instead.
  650. """
  651. def __init__(self, settings, reporter, *args, **kwargs):
  652. Element.__init__(self, *args, **kwargs)
  653. self.current_source = None
  654. """Path to or description of the input source being processed."""
  655. self.current_line = None
  656. """Line number (1-based) of `current_source`."""
  657. self.settings = settings
  658. """Runtime settings data record."""
  659. self.reporter = reporter
  660. """System message generator."""
  661. self.indirect_targets = []
  662. """List of indirect target nodes."""
  663. self.substitution_defs = {}
  664. """Mapping of substitution names to substitution_definition nodes."""
  665. self.substitution_names = {}
  666. """Mapping of case-normalized substitution names to case-sensitive
  667. names."""
  668. self.refnames = {}
  669. """Mapping of names to lists of referencing nodes."""
  670. self.refids = {}
  671. """Mapping of ids to lists of referencing nodes."""
  672. self.nameids = {}
  673. """Mapping of names to unique id's."""
  674. self.nametypes = {}
  675. """Mapping of names to hyperlink type (boolean: True => explicit,
  676. False => implicit."""
  677. self.ids = {}
  678. """Mapping of ids to nodes."""
  679. self.footnote_refs = {}
  680. """Mapping of footnote labels to lists of footnote_reference nodes."""
  681. self.citation_refs = {}
  682. """Mapping of citation labels to lists of citation_reference nodes."""
  683. self.autofootnotes = []
  684. """List of auto-numbered footnote nodes."""
  685. self.autofootnote_refs = []
  686. """List of auto-numbered footnote_reference nodes."""
  687. self.symbol_footnotes = []
  688. """List of symbol footnote nodes."""
  689. self.symbol_footnote_refs = []
  690. """List of symbol footnote_reference nodes."""
  691. self.footnotes = []
  692. """List of manually-numbered footnote nodes."""
  693. self.citations = []
  694. """List of citation nodes."""
  695. self.autofootnote_start = 1
  696. """Initial auto-numbered footnote number."""
  697. self.symbol_footnote_start = 0
  698. """Initial symbol footnote symbol index."""
  699. self.id_start = 1
  700. """Initial ID number."""
  701. self.parse_messages = []
  702. """System messages generated while parsing."""
  703. self.transform_messages = []
  704. """System messages generated while applying transforms."""
  705. import docutils.transforms
  706. self.transformer = docutils.transforms.Transformer(self)
  707. """Storage for transforms to be applied to this document."""
  708. self.decoration = None
  709. """Document's `decoration` node."""
  710. self.document = self
  711. def __getstate__(self):
  712. """
  713. Return dict with unpicklable references removed.
  714. """
  715. state = self.__dict__.copy()
  716. state['reporter'] = None
  717. state['transformer'] = None
  718. return state
  719. def asdom(self, dom=None):
  720. """Return a DOM representation of this document."""
  721. if dom is None:
  722. import xml.dom.minidom as dom
  723. domroot = dom.Document()
  724. domroot.appendChild(self._dom_node(domroot))
  725. return domroot
  726. def set_id(self, node, msgnode=None):
  727. for id in node['ids']:
  728. if self.ids.has_key(id) and self.ids[id] is not node:
  729. msg = self.reporter.severe('Duplicate ID: "%s".' % id)
  730. if msgnode != None:
  731. msgnode += msg
  732. if not node['ids']:
  733. for name in node['names']:
  734. id = self.settings.id_prefix + make_id(name)
  735. if id and not self.ids.has_key(id):
  736. break
  737. else:
  738. id = ''
  739. while not id or self.ids.has_key(id):
  740. id = (self.settings.id_prefix +
  741. self.settings.auto_id_prefix + str(self.id_start))
  742. self.id_start += 1
  743. node['ids'].append(id)
  744. self.ids[id] = node
  745. return id
  746. def set_name_id_map(self, node, id, msgnode=None, explicit=None):
  747. """
  748. `self.nameids` maps names to IDs, while `self.nametypes` maps names to
  749. booleans representing hyperlink type (True==explicit,
  750. False==implicit). This method updates the mappings.
  751. The following state transition table shows how `self.nameids` ("ids")
  752. and `self.nametypes` ("types") change with new input (a call to this
  753. method), and what actions are performed ("implicit"-type system
  754. messages are INFO/1, and "explicit"-type system messages are ERROR/3):
  755. ==== ===== ======== ======== ======= ==== ===== =====
  756. Old State Input Action New State Notes
  757. ----------- -------- ----------------- ----------- -----
  758. ids types new type sys.msg. dupname ids types
  759. ==== ===== ======== ======== ======= ==== ===== =====
  760. - - explicit - - new True
  761. - - implicit - - new False
  762. None False explicit - - new True
  763. old False explicit implicit old new True
  764. None True explicit explicit new None True
  765. old True explicit explicit new,old None True [#]_
  766. None False implicit implicit new None False
  767. old False implicit implicit new,old None False
  768. None True implicit implicit new None True
  769. old True implicit implicit new old True
  770. ==== ===== ======== ======== ======= ==== ===== =====
  771. .. [#] Do not clear the name-to-id map or invalidate the old target if
  772. both old and new targets are external and refer to identical URIs.
  773. The new target is invalidated regardless.
  774. """
  775. for name in node['names']:
  776. if self.nameids.has_key(name):
  777. self.set_duplicate_name_id(node, id, name, msgnode, explicit)
  778. else:
  779. self.nameids[name] = id
  780. self.nametypes[name] = explicit
  781. def set_duplicate_name_id(self, node, id, name, msgnode, explicit):
  782. old_id = self.nameids[name]
  783. old_explicit = self.nametypes[name]
  784. self.nametypes[name] = old_explicit or explicit
  785. if explicit:
  786. if old_explicit:
  787. level = 2
  788. if old_id is not None:
  789. old_node = self.ids[old_id]
  790. if node.has_key('refuri'):
  791. refuri = node['refuri']
  792. if old_node['names'] \
  793. and old_node.has_key('refuri') \
  794. and old_node['refuri'] == refuri:
  795. level = 1 # just inform if refuri's identical
  796. if level > 1:
  797. dupname(old_node, name)
  798. self.nameids[name] = None
  799. msg = self.reporter.system_message(
  800. level, 'Duplicate explicit target name: "%s".' % name,
  801. backrefs=[id], base_node=node)
  802. if msgnode != None:
  803. msgnode += msg
  804. dupname(node, name)
  805. else:
  806. self.nameids[name] = id
  807. if old_id is not None:
  808. old_node = self.ids[old_id]
  809. dupname(old_node, name)
  810. else:
  811. if old_id is not None and not old_explicit:
  812. self.nameids[name] = None
  813. old_node = self.ids[old_id]
  814. dupname(old_node, name)
  815. dupname(node, name)
  816. if not explicit or (not old_explicit and old_id is not None):
  817. msg = self.reporter.info(
  818. 'Duplicate implicit target name: "%s".' % name,
  819. backrefs=[id], base_node=node)
  820. if msgnode != None:
  821. msgnode += msg
  822. def has_name(self, name):
  823. return self.nameids.has_key(name)
  824. # "note" here is an imperative verb: "take note of".
  825. def note_implicit_target(self, target, msgnode=None):
  826. id = self.set_id(target, msgnode)
  827. self.set_name_id_map(target, id, msgnode, explicit=None)
  828. def note_explicit_target(self, target, msgnode=None):
  829. id = self.set_id(target, msgnode)
  830. self.set_name_id_map(target, id, msgnode, explicit=1)
  831. def note_refname(self, node):
  832. self.refnames.setdefault(node['refname'], []).append(node)
  833. def note_refid(self, node):
  834. self.refids.setdefault(node['refid'], []).append(node)
  835. def note_indirect_target(self, target):
  836. self.indirect_targets.append(target)
  837. if target['names']:
  838. self.note_refname(target)
  839. def note_anonymous_target(self, target):
  840. self.set_id(target)
  841. def note_autofootnote(self, footnote):
  842. self.set_id(footnote)
  843. self.autofootnotes.append(footnote)
  844. def note_autofootnote_ref(self, ref):
  845. self.set_id(ref)
  846. self.autofootnote_refs.append(ref)
  847. def note_symbol_footnote(self, footnote):
  848. self.set_id(footnote)
  849. self.symbol_footnotes.append(footnote)
  850. def note_symbol_footnote_ref(self, ref):
  851. self.set_id(ref)
  852. self.symbol_footnote_refs.append(ref)
  853. def note_footnote(self, footnote):
  854. self.set_id(footnote)
  855. self.footnotes.append(footnote)
  856. def note_footnote_ref(self, ref):
  857. self.set_id(ref)
  858. self.footnote_refs.setdefault(ref['refname'], []).append(ref)
  859. self.note_refname(ref)
  860. def note_citation(self, citation):
  861. self.citations.append(citation)
  862. def note_citation_ref(self, ref):
  863. self.set_id(ref)
  864. self.citation_refs.setdefault(ref['refname'], []).append(ref)
  865. self.note_refname(ref)
  866. def note_substitution_def(self, subdef, def_name, msgnode=None):
  867. name = whitespace_normalize_name(def_name)
  868. if self.substitution_defs.has_key(name):
  869. msg = self.reporter.error(
  870. 'Duplicate substitution definition name: "%s".' % name,
  871. base_node=subdef)
  872. if msgnode != None:
  873. msgnode += msg
  874. oldnode = self.substitution_defs[name]
  875. dupname(oldnode, name)
  876. # keep only the last definition:
  877. self.substitution_defs[name] = subdef
  878. # case-insensitive mapping:
  879. self.substitution_names[fully_normalize_name(name)] = name
  880. def note_substitution_ref(self, subref, refname):
  881. subref['refname'] = whitespace_normalize_name(refname)
  882. def note_pending(self, pending, priority=None):
  883. self.transformer.add_pending(pending, priority)
  884. def note_parse_message(self, message):
  885. self.parse_messages.append(message)
  886. def note_transform_message(self, message):
  887. self.transform_messages.append(message)
  888. def note_source(self, source, offset):
  889. self.current_source = source
  890. if offset is None:
  891. self.current_line = offset
  892. else:
  893. self.current_line = offset + 1
  894. def copy(self):
  895. return self.__class__(self.settings, self.reporter,
  896. **self.attributes)
  897. def get_decoration(self):
  898. if not self.decoration:
  899. self.decoration = decoration()
  900. index = self.first_child_not_matching_class(Titular)
  901. if index is None:
  902. self.append(self.decoration)
  903. else:
  904. self.insert(index, self.decoration)
  905. return self.decoration
  906. # ================
  907. # Title Elements
  908. # ================
  909. class title(Titular, PreBibliographic, TextElement): pass
  910. class subtitle(Titular, PreBibliographic, TextElement): pass
  911. class rubric(Titular, TextElement): pass
  912. # ========================
  913. # Bibliographic Elements
  914. # ========================
  915. class docinfo(Bibliographic, Element): pass
  916. class author(Bibliographic, TextElement): pass
  917. class authors(Bibliographic, Element): pass
  918. class organization(Bibliographic, TextElement): pass
  919. class address(Bibliographic, FixedTextElement): pass
  920. class contact(Bibliographic, TextElement): pass
  921. class version(Bibliographic, TextElement): pass
  922. class revision(Bibliographic, TextElement): pass
  923. class status(Bibliographic, TextElement): pass
  924. class date(Bibliographic, TextElement): pass
  925. class copyright(Bibliographic, TextElement): pass
  926. # =====================
  927. # Decorative Elements
  928. # =====================
  929. class decoration(Decorative, Element):
  930. def get_header(self):
  931. if not len(self.children) or not isinstance(self.children[0], header):
  932. self.insert(0, header())
  933. return self.children[0]
  934. def get_footer(self):
  935. if not len(self.children) or not isinstance(self.children[-1], footer):
  936. self.append(footer())
  937. return self.children[-1]
  938. class header(Decorative, Element): pass
  939. class footer(Decorative, Element): pass
  940. # =====================
  941. # Structural Elements
  942. # =====================
  943. class section(Structural, Element): pass
  944. class topic(Structural, Element):
  945. """
  946. Topics are terminal, "leaf" mini-sections, like block quotes with titles,
  947. or textual figures. A topic is just like a section, except that it has no
  948. subsections, and it doesn't have to conform to section placement rules.
  949. Topics are allowed wherever body elements (list, table, etc.) are allowed,
  950. but only at the top level of a section or document. Topics cannot nest
  951. inside topics, sidebars, or body elements; you can't have a topic inside a
  952. table, list, block quote, etc.
  953. """
  954. class sidebar(Structural, Element):
  955. """
  956. Sidebars are like miniature, parallel documents that occur inside other
  957. documents, providing related or reference material. A sidebar is
  958. typically offset by a border and "floats" to the side of the page; the
  959. document's main text may flow around it. Sidebars can also be likened to
  960. super-footnotes; their content is outside of the flow of the document's
  961. main text.
  962. Sidebars are allowed wherever body elements (list, table, etc.) are
  963. allowed, but only at the top level of a section or document. Sidebars
  964. cannot nest inside sidebars, topics, or body elements; you can't have a
  965. sidebar inside a table, list, block quote, etc.
  966. """
  967. class transition(Structural, Element): pass
  968. # ===============
  969. # Body Elements
  970. # ===============
  971. class paragraph(General, TextElement): pass
  972. class compound(General, Element): pass
  973. class container(General, Element): pass
  974. class bullet_list(Sequential, Element): pass
  975. class enumerated_list(Sequential, Element): pass
  976. class list_item(Part, Element): pass
  977. class definition_list(Sequential, Element): pass
  978. class definition_list_item(Part, Element): pass
  979. class term(Part, TextElement): pass
  980. class classifier(Part, TextElement): pass
  981. class definition(Part, Element): pass
  982. class field_list(Sequential, Element): pass
  983. class field(Part, Element): pass
  984. class field_name(Part, TextElement): pass
  985. class field_body(Part, Element): pass
  986. class option(Part, Element):
  987. child_text_separator = ''
  988. class option_argument(Part, TextElement):
  989. def astext(self):
  990. return self.get('delimiter', ' ') + TextElement.astext(self)
  991. class option_group(Part, Element):
  992. child_text_separator = ', '
  993. class option_list(Sequential, Element): pass
  994. class option_list_item(Part, Element):
  995. child_text_separator = ' '
  996. class option_string(Part, TextElement): pass
  997. class description(Part, Element): pass
  998. class literal_block(General, FixedTextElement): pass
  999. class doctest_block(General, FixedTextElement): pass
  1000. class line_block(General, Element): pass
  1001. class line(Part, TextElement):
  1002. indent = None
  1003. class block_quote(General, Element): pass
  1004. class attribution(Part, TextElement): pass
  1005. class attention(Admonition, Element): pass
  1006. class caution(Admonition, Element): pass
  1007. class danger(Admonition, Element): pass
  1008. class error(Admonition, Element): pass
  1009. class important(Admonition, Element): pass
  1010. class note(Admonition, Element): pass
  1011. class tip(Admonition, Element): pass
  1012. class hint(Admonition, Element): pass
  1013. class warning(Admonition, Element): pass
  1014. class admonition(Admonition, Element): pass
  1015. class comment(Special, Invisible, FixedTextElement): pass
  1016. class substitution_definition(Special, Invisible, TextElement): pass
  1017. class target(Special, Invisible, Inline, TextElement, Targetable): pass
  1018. class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
  1019. class citation(General, BackLinkable, Element, Labeled, Targetable): pass
  1020. class label(Part, TextElement): pass
  1021. class figure(General, Element): pass
  1022. class caption(Part, TextElement): pass
  1023. class legend(Part, Element): pass
  1024. class table(General, Element): pass
  1025. class tgroup(Part, Element): pass
  1026. class colspec(Part, Element): pass
  1027. class thead(Part, Element): pass
  1028. class tbody(Part, Element): pass
  1029. class row(Part, Element): pass
  1030. class entry(Part, Element): pass
  1031. class system_message(Special, BackLinkable, PreBibliographic, Element):
  1032. """
  1033. System message element.
  1034. Do not instantiate this class directly; use
  1035. ``document.reporter.info/warning/error/severe()`` instead.
  1036. """
  1037. def __init__(self, message=None, *children, **attributes):
  1038. if message:
  1039. p = paragraph('', message)
  1040. children = (p,) + children
  1041. try:
  1042. Element.__init__(self, '', *children, **attributes)
  1043. except:
  1044. print 'system_message: children=%r' % (children,)
  1045. raise
  1046. def astext(self):
  1047. line = self.get('line', '')
  1048. return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
  1049. self['level'], Element.astext(self))
  1050. class pending(Special, Invisible, Element):
  1051. """
  1052. The "pending" element is used to encapsulate a pending operation: the
  1053. operation (transform), the point at which to apply it, and any data it
  1054. requires. Only the pending operation's location within the document is
  1055. stored in the public document tree (by the "pending" object itself); the
  1056. operation and its data are stored in the "pending" object's internal
  1057. instance attributes.
  1058. For example, say you want a table of contents in your reStructuredText
  1059. document. The easiest way to specify where to put it is from within the
  1060. document, with a directive::
  1061. .. contents::
  1062. But the "contents" directive can't do its work until the entire document
  1063. has been parsed and possibly transformed to some extent. So the directive
  1064. code leaves a placeholder behind that will trigger the second phase of its
  1065. processing, something like this::
  1066. <pending ...public attributes...> + internal attributes
  1067. Use `document.note_pending()` so that the
  1068. `docutils.transforms.Transformer` stage of processing can run all pending
  1069. transforms.
  1070. """
  1071. def __init__(self, transform, details=None,
  1072. rawsource='', *children, **attributes):
  1073. Element.__init__(self, rawsource, *children, **attributes)
  1074. self.transform = transform
  1075. """The `docutils.transforms.Transform` class implementing the pending
  1076. operation."""
  1077. self.details = details or {}
  1078. """Detail data (dictionary) required by the pending operation."""
  1079. def pformat(self, indent=' ', level=0):
  1080. internals = [
  1081. '.. internal attributes:',
  1082. ' .transform: %s.%s' % (self.transform.__module__,
  1083. self.transform.__name__),
  1084. ' .details:']
  1085. details = self.details.items()
  1086. details.sort()
  1087. for key, value in details:
  1088. if isinstance(value, Node):
  1089. internals.append('%7s%s:' % ('', key))
  1090. internals.extend(['%9s%s' % ('', line)
  1091. for line in value.pformat().splitlines()])
  1092. elif value and isinstance(value, ListType) \
  1093. and isinstance(value[0], Node):
  1094. internals.append('%7s%s:' % ('', key))
  1095. for v in value:
  1096. internals.extend(['%9s%s' % ('', line)
  1097. for line in v.pformat().splitlines()])
  1098. else:
  1099. internals.append('%7s%s: %r' % ('', key, value))
  1100. return (Element.pformat(self, indent, level)
  1101. + ''.join([(' %s%s\n' % (indent * level, line))
  1102. for line in internals]))
  1103. def copy(self):
  1104. return self.__class__(self.transform, self.details, self.rawsource,
  1105. **self.attributes)
  1106. class raw(Special, Inline, PreBibliographic, FixedTextElement):
  1107. """
  1108. Raw data that is to be passed untouched to the Writer.
  1109. """
  1110. pass
  1111. # =================
  1112. # Inline Elements
  1113. # =================
  1114. class emphasis(Inline, TextElement): pass
  1115. class strong(Inline, TextElement): pass
  1116. class literal(Inline, TextElement): pass
  1117. class reference(General, Inline, Referential, TextElement): pass
  1118. class footnote_reference(Inline, Referential, TextElement): pass
  1119. class citation_reference(Inline, Referential, TextElement): pass
  1120. class substitution_reference(Inline, TextElement): pass
  1121. class title_reference(Inline, TextElement): pass
  1122. class abbreviation(Inline, TextElement): pass
  1123. class acronym(Inline, TextElement): pass
  1124. class superscript(Inline, TextElement): pass
  1125. class subscript(Inline, TextElement): pass
  1126. class image(General, Inline, Element):
  1127. def astext(self):
  1128. return self.get('alt', '')
  1129. class inline(Inline, TextElement): pass
  1130. class problematic(Inline, TextElement): pass
  1131. class generated(Inline, TextElement): pass
  1132. # ========================================
  1133. # Auxiliary Classes, Functions, and Data
  1134. # ========================================
  1135. node_class_names = """
  1136. Text
  1137. abbreviation acronym address admonition attention attribution author
  1138. authors
  1139. block_quote bullet_list
  1140. caption caution citation citation_reference classifier colspec comment
  1141. compound contact container copyright
  1142. danger date decoration definition definition_list definition_list_item
  1143. description docinfo doctest_block document
  1144. emphasis entry enumerated_list error
  1145. field field_body field_list field_name figure footer
  1146. footnote footnote_reference
  1147. generated
  1148. header hint
  1149. image important inline
  1150. label legend line line_block list_item literal literal_block
  1151. note
  1152. option option_argument option_group option_list option_list_item
  1153. option_string organization
  1154. paragraph pending problematic
  1155. raw reference revision row rubric
  1156. section sidebar status strong subscript substitution_definition
  1157. substitution_reference subtitle superscript system_message
  1158. table target tbody term tgroup thead tip title title_reference topic
  1159. transition
  1160. version
  1161. warning""".split()
  1162. """A list of names of all concrete Node subclasses."""
  1163. class NodeVisitor:
  1164. """
  1165. "Visitor" pattern [GoF95]_ abstract superclass implementation for
  1166. document tree traversals.
  1167. Each node class has corresponding methods, doing nothing by
  1168. default; override individual methods for specific and useful
  1169. behaviour. The `dispatch_visit()` method is called by
  1170. `Node.walk()` upon entering a node. `Node.walkabout()` also calls
  1171. the `dispatch_departure()` method before exiting a node.
  1172. The dispatch methods call "``visit_`` + node class name" or
  1173. "``depart_`` + node class name", resp.
  1174. This is a base class for visitors whose ``visit_...`` & ``depart_...``
  1175. methods should be implemented for *all* node types encountered (such as
  1176. for `docutils.writers.Writer` subclasses). Unimplemented methods will
  1177. raise exceptions.
  1178. For sparse traversals, where only certain node types are of interest,
  1179. subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform
  1180. processing is desired, subclass `GenericNodeVisitor`.
  1181. .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
  1182. Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
  1183. 1995.
  1184. """
  1185. optional = ()
  1186. """
  1187. Tuple containing node class names (as strings).
  1188. No exception will be raised if writers do not implement visit
  1189. or departure functions for these node classes.
  1190. Used to ensure transitional compatibility with existing 3rd-party writers.
  1191. """
  1192. def __init__(self, document):
  1193. self.document = document
  1194. def dispatch_visit(self, node):
  1195. """
  1196. Call self."``visit_`` + node class name" with `node` as
  1197. parameter. If the ``visit_...`` method does not exist, call
  1198. self.unknown_visit.
  1199. """
  1200. node_name = node.__class__.__name__
  1201. method = getattr(self, 'visit_' + node_name, self.unknown_visit)
  1202. self.document.reporter.debug(
  1203. 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
  1204. % (method.__name__, node_name))
  1205. return method(node)
  1206. def dispatch_departure(self, node):
  1207. """
  1208. Call self."``depart_`` + node class name" with `node` as
  1209. parameter. If the ``depart_...`` method does not exist, call
  1210. self.unknown_departure.
  1211. """
  1212. node_name = node.__class__.__name__
  1213. method = getattr(self, 'depart_' + node_name, self.unknown_departure)
  1214. self.document.reporter.debug(
  1215. 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
  1216. % (method.__name__, node_name))
  1217. return method(node)
  1218. def unknown_visit(self, node):
  1219. """
  1220. Called when entering unknown `Node` types.
  1221. Raise an exception unless overridden.
  1222. """
  1223. if (node.document.settings.strict_visitor
  1224. or node.__class__.__name__ not in self.optional):
  1225. raise NotImplementedError(
  1226. '%s visiting unknown node type: %s'
  1227. % (self.__class__, node.__class__.__name__))
  1228. def unknown_departure(self, node):
  1229. """
  1230. Called before exiting unknown `Node` types.
  1231. Raise exception unless overridden.
  1232. """
  1233. if (node.document.settings.strict_visitor
  1234. or node.__class__.__name__ not in self.optional):
  1235. raise NotImplementedError(
  1236. '%s departing unknown node type: %s'
  1237. % (self.__class__, node.__class__.__name__))
  1238. class SparseNodeVisitor(NodeVisitor):
  1239. """
  1240. Base class for sparse traversals, where only certain node types are of
  1241. interest. When ``visit_...`` & ``depart_...`` methods should be
  1242. implemented for *all* node types (such as for `docutils.writers.Writer`
  1243. subclasses), subclass `NodeVisitor` instead.
  1244. """
  1245. class GenericNodeVisitor(NodeVisitor):
  1246. """
  1247. Generic "Visitor" abstract superclass, for simple traversals.
  1248. Unless overridden, each ``visit_...`` method calls `default_visit()`, and
  1249. each ``depart_...`` method (when using `Node.walkabout()`) calls
  1250. `default_departure()`. `default_visit()` (and `default_departure()`) must
  1251. be overridden in subclasses.
  1252. Define fully generic visitors by overriding `default_visit()` (and
  1253. `default_departure()`) only. Define semi-generic visitors by overriding
  1254. individual ``visit_...()`` (and ``depart_...()``) methods also.
  1255. `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should
  1256. be overridden for default behavior.
  1257. """
  1258. def default_visit(self, node):
  1259. """Override for generic, uniform traversals."""
  1260. raise NotImplementedError
  1261. def default_departure(self, node):
  1262. """Override for generic, uniform traversals."""
  1263. raise NotImplementedError
  1264. def _call_default_visit(self, node):
  1265. self.default_visit(node)
  1266. def _call_default_departure(self, node):
  1267. self.default_departure(node)
  1268. def _nop(self, node):
  1269. pass
  1270. def _add_node_class_names(names):
  1271. """Save typing with dynamic assignments:"""
  1272. for _name in names:
  1273. setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
  1274. setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
  1275. setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
  1276. setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
  1277. _add_node_class_names(node_class_names)
  1278. class TreeCopyVisitor(GenericNodeVisitor):
  1279. """
  1280. Make a complete copy of a tree or branch, including element attributes.
  1281. """
  1282. def __init__(self, document):
  1283. GenericNodeVisitor.__init__(self, document)
  1284. self.parent_stack = []
  1285. self.parent = []
  1286. def get_tree_copy(self):
  1287. return self.parent[0]
  1288. def default_visit(self, node):
  1289. """Copy the current node, and make it the new acting parent."""
  1290. newnode = node.copy()
  1291. self.parent.append(newnode)
  1292. self.parent_stack.append(self.parent)
  1293. self.parent = newnode
  1294. def default_departure(self, node):
  1295. """Restore the previous acting parent."""
  1296. self.parent = self.parent_stack.pop()
  1297. class TreePruningException(Exception):
  1298. """
  1299. Base class for `NodeVisitor`-related tree pruning exceptions.
  1300. Raise subclasses from within ``visit_...`` or ``depart_...`` methods
  1301. called from `Node.walk()` and `Node.walkabout()` tree traversals to prune
  1302. the tree traversed.
  1303. """
  1304. pass
  1305. class SkipChildren(TreePruningException):
  1306. """
  1307. Do not visit any children of the current node. The current node's
  1308. siblings and ``depart_...`` method are not affected.
  1309. """
  1310. pass
  1311. class SkipSiblings(TreePruningException):
  1312. """
  1313. Do not visit any more siblings (to the right) of the current node. The
  1314. current node's children and its ``depart_...`` method are not affected.
  1315. """
  1316. pass
  1317. class SkipNode(TreePruningException):
  1318. """
  1319. Do not visit the current node's children, and do not call the current
  1320. node's ``depart_...`` method.
  1321. """
  1322. pass
  1323. class SkipDeparture(TreePruningException):
  1324. """
  1325. Do not call the current node's ``depart_...`` method. The current node's
  1326. children and siblings are not affected.
  1327. """
  1328. pass
  1329. class NodeFound(TreePruningException):
  1330. """
  1331. Raise to indicate that the target of a search has been found. This
  1332. exception must be caught by the client; it is not caught by the traversal
  1333. code.
  1334. """
  1335. pass
  1336. class StopTraversal(TreePruningException):
  1337. """
  1338. Stop the traversal alltogether. The current node's ``depart_...`` method
  1339. is not affected. The parent nodes ``depart_...`` methods are also called
  1340. as usual. No other nodes are visited. This is an alternative to
  1341. NodeFound that does not cause exception handling to trickle up to the
  1342. caller.
  1343. """
  1344. pass
  1345. def make_id(string):
  1346. """
  1347. Convert `string` into an identifier and return it.
  1348. Docutils identifiers will conform to the regular expression
  1349. ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class"
  1350. and "id" attributes) should have no underscores, colons, or periods.
  1351. Hyphens may be used.
  1352. - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
  1353. ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
  1354. followed by any number of letters, digits ([0-9]), hyphens ("-"),
  1355. underscores ("_"), colons (":"), and periods (".").
  1356. - However the `CSS1 spec`_ defines identifiers based on the "name" token,
  1357. a tighter interpretation ("flex" tokenizer notation; "latin1" and
  1358. "escape" 8-bit characters have been replaced with entities)::
  1359. unicode \\[0-9a-f]{1,4}
  1360. latin1 [&iexcl;-&yuml;]
  1361. escape {unicode}|\\[ -~&iexcl;-&yuml;]
  1362. nmchar [-a-z0-9]|{latin1}|{escape}
  1363. name {nmchar}+
  1364. The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"),
  1365. or periods ("."), therefore "class" and "id" attributes should not contain
  1366. these characters. They should be replaced with hyphens ("-"). Combined
  1367. with HTML's requirements (the first character must be a letter; no
  1368. "unicode", "latin1", or "escape" characters), this results in the
  1369. ``[a-z](-?[a-z0-9]+)*`` pattern.
  1370. .. _HTML 4.01 spec: http://www.w3.org/TR/html401
  1371. .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
  1372. """
  1373. id = _non_id_chars.sub('-', ' '.join(string.lower().split()))
  1374. id = _non_id_at_ends.sub('', id)
  1375. return str(id)
  1376. _non_id_chars = re.compile('[^a-z0-9]+')
  1377. _non_id_at_ends = re.compile('^[-0-9]+|-+$')
  1378. def dupname(node, name):
  1379. node['dupnames'].append(name)
  1380. node['names'].remove(name)
  1381. # Assume that this method is referenced, even though it isn't; we
  1382. # don't want to throw unnecessary system_messages.
  1383. node.referenced = 1
  1384. def fully_normalize_name(name):
  1385. """Return a case- and whitespace-normalized name."""
  1386. return ' '.join(name.lower().split())
  1387. def whitespace_normalize_name(name):
  1388. """Return a whitespace-normalized name."""
  1389. return ' '.join(name.split())
  1390. def serial_escape(value):
  1391. """Escape string values that are elements of a list, for serialization."""
  1392. return value.replace('\\', r'\\').replace(' ', r'\ ')
  1393. #
  1394. #
  1395. # Local Variables:
  1396. # indent-tabs-mode: nil
  1397. # sentence-end-double-space: t
  1398. # fill-column: 78
  1399. # End: