comparison venv/lib/python2.7/site-packages/docutils/nodes.py @ 0:d67268158946 draft

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