Fix [ 251 ] system_message.copy() TypeError. Fix nodes.Element.copy()
[docutils.git] / docutils / docutils / nodes.py
blob1490f43fbdac0728c1c96046bb7330f90e9380f8
1 # $Id$
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.
6 """
7 Docutils document tree element class library.
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_.
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.
20 .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
21 """
23 __docformat__ = 'reStructuredText'
25 import sys
26 import os
27 import re
28 import warnings
29 import types
30 import unicodedata
32 # ==============================
33 # Functional Node Base Classes
34 # ==============================
36 class Node(object):
38 """Abstract base class of nodes in a document tree."""
40 parent = None
41 """Back-reference to the Node immediately containing this Node."""
43 document = None
44 """The `document` node at the root of the tree containing this Node."""
46 source = None
47 """Path or description of the input source which generated this Node."""
49 line = None
50 """The line number (1-based) of the beginning of this Node in `source`."""
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.
58 Use `len()` to check node length. Use `None` to represent a boolean
59 false value.
60 """
61 return True
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')
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)
76 def pformat(self, indent=' ', level=0):
77 """
78 Return an indented pseudo-XML representation, for test purposes.
80 Override in subclasses.
81 """
82 raise NotImplementedError
84 def copy(self):
85 """Return a copy of self."""
86 raise NotImplementedError
88 def deepcopy(self):
89 """Return a deep copy of self (also copying children)."""
90 raise NotImplementedError
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
101 def walk(self, visitor):
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.)
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.
115 Within ``visit`` methods (and ``depart`` methods for
116 `walkabout()`), `TreePruningException` subclasses may be raised
117 (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
119 Parameter `visitor`: A `NodeVisitor` object, containing a
120 ``visit`` implementation for each `Node` subclass encountered.
122 Return true if we should stop the traversal.
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
147 def walkabout(self, visitor):
149 Perform a tree traversal similarly to `Node.walk()` (which
150 see), except also call the `dispatch_departure()` method
151 before exiting each node.
153 Parameter `visitor`: A `NodeVisitor` object, containing a
154 ``visit`` and ``depart`` implementation for each `Node`
155 subclass encountered.
157 Return true if we should stop the traversal.
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
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
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
207 def traverse(self, condition=None, include_self=True, descend=True,
208 siblings=False, ascend=False):
210 Return an iterable containing
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
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)``.
224 If ascend is true, assume siblings to be true as well.
226 For example, given the following tree::
228 <paragraph>
229 <emphasis> <--- emphasis.traverse() and
230 <strong> <--- strong.traverse() are called.
233 <reference name="Baz" refid="baz">
236 Then list(emphasis.traverse()) equals ::
238 [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
240 and list(strong.traverse(ascend=True)) equals ::
242 [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
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
282 def next_node(self, condition=None, include_self=False, descend=True,
283 siblings=False, ascend=False):
285 Return the first node in the iterable returned by traverse(),
286 or None if the iterable is empty.
288 Parameter list is the same as of traverse. Note that
289 include_self defaults to 0, though.
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
299 if sys.version_info < (3,):
300 class reprunicode(unicode):
302 A unicode sub-class that removes the initial u from unicode's repr.
305 def __repr__(self):
306 return unicode.__repr__(self)[1:]
307 else:
308 reprunicode = unicode
311 def ensure_str(s):
313 Failsave conversion of `unicode` to `str`.
315 if sys.version_info < (3,) and isinstance(s, unicode):
316 return s.encode('ascii', 'backslashreplace')
317 return s
320 class Text(Node, reprunicode):
323 Instances are terminal nodes (leaves) containing text only; no child
324 nodes or attributes. Initialize by passing a string to the constructor.
325 Access the text itself with the `astext` method.
328 tagname = '#text'
330 children = ()
331 """Text nodes have no children, and cannot have children."""
333 if sys.version_info > (3,):
334 def __new__(cls, data, rawsource=None):
335 """Prevent the rawsource argument from propagating to str."""
336 if isinstance(data, bytes):
337 raise TypeError('expecting str data, not bytes')
338 return reprunicode.__new__(cls, data)
339 else:
340 def __new__(cls, data, rawsource=None):
341 """Prevent the rawsource argument from propagating to str."""
342 return reprunicode.__new__(cls, data)
344 def __init__(self, data, rawsource=''):
345 self.rawsource = rawsource
346 """The raw text from which this element was constructed."""
348 def shortrepr(self, maxlen=18):
349 data = self
350 if len(data) > maxlen:
351 data = data[:maxlen-4] + ' ...'
352 return '<%s: %r>' % (self.tagname, reprunicode(data))
354 def __repr__(self):
355 return self.shortrepr(maxlen=68)
357 def _dom_node(self, domroot):
358 return domroot.createTextNode(unicode(self))
360 def astext(self):
361 return reprunicode(self)
363 # Note about __unicode__: The implementation of __unicode__ here,
364 # and the one raising NotImplemented in the superclass Node had
365 # to be removed when changing Text to a subclass of unicode instead
366 # of UserString, since there is no way to delegate the __unicode__
367 # call to the superclass unicode:
368 # unicode itself does not have __unicode__ method to delegate to
369 # and calling unicode(self) or unicode.__new__ directly creates
370 # an infinite loop
372 def copy(self):
373 return self.__class__(reprunicode(self), rawsource=self.rawsource)
375 def deepcopy(self):
376 return self.copy()
378 def pformat(self, indent=' ', level=0):
379 result = []
380 indent = indent * level
381 for line in self.splitlines():
382 result.append(indent + line + '\n')
383 return ''.join(result)
385 # rstrip and lstrip are used by substitution definitions where
386 # they are expected to return a Text instance, this was formerly
387 # taken care of by UserString.
389 def rstrip(self, chars=None):
390 node = self.__class__(reprunicode.rstrip(self, chars))
391 node.rawsource = self.rawsource.rstrip((chars or ' \n\t\r')+'\\')
392 return node
393 def lstrip(self, chars=None):
394 node = self.__class__(reprunicode.lstrip(self, chars))
395 node.rawsource = re.sub(ur'^(\\?[%s])+'%(chars or ' \n\t\r'), u'',
396 self.rawsource)
397 return node
399 class Element(Node):
402 `Element` is the superclass to all specific elements.
404 Elements contain attributes and child nodes. Elements emulate
405 dictionaries for attributes, indexing by attribute name (a string). To
406 set the attribute 'att' to 'value', do::
408 element['att'] = 'value'
410 There are two special attributes: 'ids' and 'names'. Both are
411 lists of unique identifiers, and names serve as human interfaces
412 to IDs. Names are case- and whitespace-normalized (see the
413 fully_normalize_name() function), and IDs conform to the regular
414 expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
416 Elements also emulate lists for child nodes (element nodes and/or text
417 nodes), indexing by integer. To get the first child node, use::
419 element[0]
421 Elements may be constructed using the ``+=`` operator. To add one new
422 child node to element, do::
424 element += node
426 This is equivalent to ``element.append(node)``.
428 To add a list of multiple child nodes at once, use the same ``+=``
429 operator::
431 element += [node1, node2]
433 This is equivalent to ``element.extend([node1, node2])``.
436 basic_attributes = ('ids', 'classes', 'names', 'dupnames')
437 """List attributes which are defined for every Element-derived class
438 instance and can be safely transferred to a different node."""
440 local_attributes = ('backrefs',)
441 """A list of class-specific attributes that should not be copied with the
442 standard attributes when replacing a node.
444 NOTE: Derived classes should override this value to prevent any of its
445 attributes being copied by adding to the value in its parent class."""
447 list_attributes = basic_attributes + local_attributes
448 """List attributes, automatically initialized to empty lists for
449 all nodes."""
451 known_attributes = list_attributes + ('source', 'rawsource')
452 """List attributes that are known to the Element base class."""
454 tagname = None
455 """The element generic identifier. If None, it is set as an instance
456 attribute to the name of the class."""
458 child_text_separator = '\n\n'
459 """Separator for child nodes, used by `astext()` method."""
461 def __init__(self, rawsource='', *children, **attributes):
462 self.rawsource = rawsource
463 """The raw text from which this element was constructed.
465 NOTE: some elements do not set this value (default '').
468 self.children = []
469 """List of child nodes (elements and/or `Text`)."""
471 self.extend(children) # maintain parent info
473 self.attributes = {}
474 """Dictionary of attribute {name: value}."""
476 # Initialize list attributes.
477 for att in self.list_attributes:
478 self.attributes[att] = []
480 for att, value in attributes.items():
481 att = att.lower()
482 if att in self.list_attributes:
483 # mutable list; make a copy for this node
484 self.attributes[att] = value[:]
485 else:
486 self.attributes[att] = value
488 if self.tagname is None:
489 self.tagname = self.__class__.__name__
491 def _dom_node(self, domroot):
492 element = domroot.createElement(self.tagname)
493 for attribute, value in self.attlist():
494 if isinstance(value, list):
495 value = ' '.join([serial_escape('%s' % (v,)) for v in value])
496 element.setAttribute(attribute, '%s' % value)
497 for child in self.children:
498 element.appendChild(child._dom_node(domroot))
499 return element
501 def __repr__(self):
502 data = ''
503 for c in self.children:
504 data += c.shortrepr()
505 if len(data) > 60:
506 data = data[:56] + ' ...'
507 break
508 if self['names']:
509 return '<%s "%s": %s>' % (self.__class__.__name__,
510 '; '.join([ensure_str(n) for n in self['names']]), data)
511 else:
512 return '<%s: %s>' % (self.__class__.__name__, data)
514 def shortrepr(self):
515 if self['names']:
516 return '<%s "%s"...>' % (self.__class__.__name__,
517 '; '.join([ensure_str(n) for n in self['names']]))
518 else:
519 return '<%s...>' % self.tagname
521 def __unicode__(self):
522 if self.children:
523 return u'%s%s%s' % (self.starttag(),
524 ''.join([unicode(c) for c in self.children]),
525 self.endtag())
526 else:
527 return self.emptytag()
529 if sys.version_info > (3,):
530 # 2to3 doesn't convert __unicode__ to __str__
531 __str__ = __unicode__
533 def starttag(self, quoteattr=None):
534 # the optional arg is used by the docutils_xml writer
535 if quoteattr is None:
536 quoteattr = pseudo_quoteattr
537 parts = [self.tagname]
538 for name, value in self.attlist():
539 if value is None: # boolean attribute
540 parts.append('%s="True"' % name)
541 continue
542 if isinstance(value, list):
543 values = [serial_escape('%s' % (v,)) for v in value]
544 value = ' '.join(values)
545 else:
546 value = unicode(value)
547 value = quoteattr(value)
548 parts.append(u'%s=%s' % (name, value))
549 return u'<%s>' % u' '.join(parts)
551 def endtag(self):
552 return '</%s>' % self.tagname
554 def emptytag(self):
555 return u'<%s/>' % u' '.join([self.tagname] +
556 ['%s="%s"' % (n, v)
557 for n, v in self.attlist()])
559 def __len__(self):
560 return len(self.children)
562 def __contains__(self, key):
563 # support both membership test for children and attributes
564 # (has_key is translated to "in" by 2to3)
565 if isinstance(key, basestring):
566 return key in self.attributes
567 return key in self.children
569 def __getitem__(self, key):
570 if isinstance(key, basestring):
571 return self.attributes[key]
572 elif isinstance(key, int):
573 return self.children[key]
574 elif isinstance(key, types.SliceType):
575 assert key.step in (None, 1), 'cannot handle slice with stride'
576 return self.children[key.start:key.stop]
577 else:
578 raise TypeError, ('element index must be an integer, a slice, or '
579 'an attribute name string')
581 def __setitem__(self, key, item):
582 if isinstance(key, basestring):
583 self.attributes[str(key)] = item
584 elif isinstance(key, int):
585 self.setup_child(item)
586 self.children[key] = item
587 elif isinstance(key, types.SliceType):
588 assert key.step in (None, 1), 'cannot handle slice with stride'
589 for node in item:
590 self.setup_child(node)
591 self.children[key.start:key.stop] = item
592 else:
593 raise TypeError, ('element index must be an integer, a slice, or '
594 'an attribute name string')
596 def __delitem__(self, key):
597 if isinstance(key, basestring):
598 del self.attributes[key]
599 elif isinstance(key, int):
600 del self.children[key]
601 elif isinstance(key, types.SliceType):
602 assert key.step in (None, 1), 'cannot handle slice with stride'
603 del self.children[key.start:key.stop]
604 else:
605 raise TypeError, ('element index must be an integer, a simple '
606 'slice, or an attribute name string')
608 def __add__(self, other):
609 return self.children + other
611 def __radd__(self, other):
612 return other + self.children
614 def __iadd__(self, other):
615 """Append a node or a list of nodes to `self.children`."""
616 if isinstance(other, Node):
617 self.append(other)
618 elif other is not None:
619 self.extend(other)
620 return self
622 def astext(self):
623 return self.child_text_separator.join(
624 [child.astext() for child in self.children])
626 def non_default_attributes(self):
627 atts = {}
628 for key, value in self.attributes.items():
629 if self.is_not_default(key):
630 atts[key] = value
631 return atts
633 def attlist(self):
634 attlist = self.non_default_attributes().items()
635 attlist.sort()
636 return attlist
638 def get(self, key, failobj=None):
639 return self.attributes.get(key, failobj)
641 def hasattr(self, attr):
642 return attr in self.attributes
644 def delattr(self, attr):
645 if attr in self.attributes:
646 del self.attributes[attr]
648 def setdefault(self, key, failobj=None):
649 return self.attributes.setdefault(key, failobj)
651 has_key = hasattr
653 # support operator ``in``
654 __contains__ = hasattr
656 def get_language_code(self, fallback=''):
657 """Return node's language tag.
659 Look iteratively in self and parents for a class argument
660 starting with ``language-`` and return the remainder of it
661 (which should be a `BCP49` language tag) or the `fallback`.
663 for cls in self.get('classes', []):
664 if cls.startswith('language-'):
665 return cls[9:]
666 try:
667 return self.parent.get_language(fallback)
668 except AttributeError:
669 return fallback
671 def append(self, item):
672 self.setup_child(item)
673 self.children.append(item)
675 def extend(self, item):
676 for node in item:
677 self.append(node)
679 def insert(self, index, item):
680 if isinstance(item, Node):
681 self.setup_child(item)
682 self.children.insert(index, item)
683 elif item is not None:
684 self[index:index] = item
686 def pop(self, i=-1):
687 return self.children.pop(i)
689 def remove(self, item):
690 self.children.remove(item)
692 def index(self, item):
693 return self.children.index(item)
695 def is_not_default(self, key):
696 if self[key] == [] and key in self.list_attributes:
697 return 0
698 else:
699 return 1
701 def update_basic_atts(self, dict_):
703 Update basic attributes ('ids', 'names', 'classes',
704 'dupnames', but not 'source') from node or dictionary `dict_`.
706 if isinstance(dict_, Node):
707 dict_ = dict_.attributes
708 for att in self.basic_attributes:
709 self.append_attr_list(att, dict_.get(att, []))
711 def append_attr_list(self, attr, values):
713 For each element in values, if it does not exist in self[attr], append
716 NOTE: Requires self[attr] and values to be sequence type and the
717 former should specifically be a list.
719 # List Concatenation
720 for value in values:
721 if not value in self[attr]:
722 self[attr].append(value)
724 def coerce_append_attr_list(self, attr, value):
726 First, convert both self[attr] and value to a non-string sequence
727 type; if either is not already a sequence, convert it to a list of one
728 element. Then call append_attr_list.
730 NOTE: self[attr] and value both must not be None.
732 # List Concatenation
733 if not isinstance(self.get(attr), list):
734 self[attr] = [self[attr]]
735 if not isinstance(value, list):
736 value = [value]
737 self.append_attr_list(attr, value)
739 def replace_attr(self, attr, value, force = True):
741 If self[attr] does not exist or force is True or omitted, set
742 self[attr] to value, otherwise do nothing.
744 # One or the other
745 if force or self.get(attr) is None:
746 self[attr] = value
748 def copy_attr_convert(self, attr, value, replace = True):
750 If attr is an attribute of self, set self[attr] to
751 [self[attr], value], otherwise set self[attr] to value.
753 NOTE: replace is not used by this function and is kept only for
754 compatibility with the other copy functions.
756 if self.get(attr) is not value:
757 self.coerce_append_attr_list(attr, value)
759 def copy_attr_coerce(self, attr, value, replace):
761 If attr is an attribute of self and either self[attr] or value is a
762 list, convert all non-sequence values to a sequence of 1 element and
763 then concatenate the two sequence, setting the result to self[attr].
764 If both self[attr] and value are non-sequences and replace is True or
765 self[attr] is None, replace self[attr] with value. Otherwise, do
766 nothing.
768 if self.get(attr) is not value:
769 if isinstance(self.get(attr), list) or \
770 isinstance(value, list):
771 self.coerce_append_attr_list(attr, value)
772 else:
773 self.replace_attr(attr, value, replace)
775 def copy_attr_concatenate(self, attr, value, replace):
777 If attr is an attribute of self and both self[attr] and value are
778 lists, concatenate the two sequences, setting the result to
779 self[attr]. If either self[attr] or value are non-sequences and
780 replace is True or self[attr] is None, replace self[attr] with value.
781 Otherwise, do nothing.
783 if self.get(attr) is not value:
784 if isinstance(self.get(attr), list) and \
785 isinstance(value, list):
786 self.append_attr_list(attr, value)
787 else:
788 self.replace_attr(attr, value, replace)
790 def copy_attr_consistent(self, attr, value, replace):
792 If replace is True or self[attr] is None, replace self[attr] with
793 value. Otherwise, do nothing.
795 if self.get(attr) is not value:
796 self.replace_attr(attr, value, replace)
798 def update_all_atts(self, dict_, update_fun = copy_attr_consistent,
799 replace = True, and_source = False):
801 Updates all attributes from node or dictionary `dict_`.
803 Appends the basic attributes ('ids', 'names', 'classes',
804 'dupnames', but not 'source') and then, for all other attributes in
805 dict_, updates the same attribute in self. When attributes with the
806 same identifier appear in both self and dict_, the two values are
807 merged based on the value of update_fun. Generally, when replace is
808 True, the values in self are replaced or merged with the values in
809 dict_; otherwise, the values in self may be preserved or merged. When
810 and_source is True, the 'source' attribute is included in the copy.
812 NOTE: When replace is False, and self contains a 'source' attribute,
813 'source' is not replaced even when dict_ has a 'source'
814 attribute, though it may still be merged into a list depending
815 on the value of update_fun.
816 NOTE: It is easier to call the update-specific methods then to pass
817 the update_fun method to this function.
819 if isinstance(dict_, Node):
820 dict_ = dict_.attributes
822 # Include the source attribute when copying?
823 if and_source:
824 filter_fun = self.is_not_list_attribute
825 else:
826 filter_fun = self.is_not_known_attribute
828 # Copy the basic attributes
829 self.update_basic_atts(dict_)
831 # Grab other attributes in dict_ not in self except the
832 # (All basic attributes should be copied already)
833 for att in filter(filter_fun, dict_):
834 update_fun(self, att, dict_[att], replace)
836 def update_all_atts_consistantly(self, dict_, replace = True,
837 and_source = False):
839 Updates all attributes from node or dictionary `dict_`.
841 Appends the basic attributes ('ids', 'names', 'classes',
842 'dupnames', but not 'source') and then, for all other attributes in
843 dict_, updates the same attribute in self. When attributes with the
844 same identifier appear in both self and dict_ and replace is True, the
845 values in self are replaced with the values in dict_; otherwise, the
846 values in self are preserved. When and_source is True, the 'source'
847 attribute is included in the copy.
849 NOTE: When replace is False, and self contains a 'source' attribute,
850 'source' is not replaced even when dict_ has a 'source'
851 attribute, though it may still be merged into a list depending
852 on the value of update_fun.
854 self.update_all_atts(dict_, Element.copy_attr_consistent, replace,
855 and_source)
857 def update_all_atts_concatenating(self, dict_, replace = True,
858 and_source = False):
860 Updates all attributes from node or dictionary `dict_`.
862 Appends the basic attributes ('ids', 'names', 'classes',
863 'dupnames', but not 'source') and then, for all other attributes in
864 dict_, updates the same attribute in self. When attributes with the
865 same identifier appear in both self and dict_ whose values aren't each
866 lists and replace is True, the values in self are replaced with the
867 values in dict_; if the values from self and dict_ for the given
868 identifier are both of list type, then the two lists are concatenated
869 and the result stored in self; otherwise, the values in self are
870 preserved. When and_source is True, the 'source' attribute is
871 included in the copy.
873 NOTE: When replace is False, and self contains a 'source' attribute,
874 'source' is not replaced even when dict_ has a 'source'
875 attribute, though it may still be merged into a list depending
876 on the value of update_fun.
878 self.update_all_atts(dict_, Element.copy_attr_concatenate, replace,
879 and_source)
881 def update_all_atts_coercion(self, dict_, replace = True,
882 and_source = False):
884 Updates all attributes from node or dictionary `dict_`.
886 Appends the basic attributes ('ids', 'names', 'classes',
887 'dupnames', but not 'source') and then, for all other attributes in
888 dict_, updates the same attribute in self. When attributes with the
889 same identifier appear in both self and dict_ whose values are both
890 not lists and replace is True, the values in self are replaced with
891 the values in dict_; if either of the values from self and dict_ for
892 the given identifier are of list type, then first any non-lists are
893 converted to 1-element lists and then the two lists are concatenated
894 and the result stored in self; otherwise, the values in self are
895 preserved. When and_source is True, the 'source' attribute is
896 included in the copy.
898 NOTE: When replace is False, and self contains a 'source' attribute,
899 'source' is not replaced even when dict_ has a 'source'
900 attribute, though it may still be merged into a list depending
901 on the value of update_fun.
903 self.update_all_atts(dict_, Element.copy_attr_coerce, replace,
904 and_source)
906 def update_all_atts_convert(self, dict_, and_source = False):
908 Updates all attributes from node or dictionary `dict_`.
910 Appends the basic attributes ('ids', 'names', 'classes',
911 'dupnames', but not 'source') and then, for all other attributes in
912 dict_, updates the same attribute in self. When attributes with the
913 same identifier appear in both self and dict_ then first any non-lists
914 are converted to 1-element lists and then the two lists are
915 concatenated and the result stored in self; otherwise, the values in
916 self are preserved. When and_source is True, the 'source' attribute
917 is included in the copy.
919 NOTE: When replace is False, and self contains a 'source' attribute,
920 'source' is not replaced even when dict_ has a 'source'
921 attribute, though it may still be merged into a list depending
922 on the value of update_fun.
924 self.update_all_atts(dict_, Element.copy_attr_convert,
925 and_source = and_source)
927 def clear(self):
928 self.children = []
930 def replace(self, old, new):
931 """Replace one child `Node` with another child or children."""
932 index = self.index(old)
933 if isinstance(new, Node):
934 self.setup_child(new)
935 self[index] = new
936 elif new is not None:
937 self[index:index+1] = new
939 def replace_self(self, new):
941 Replace `self` node with `new`, where `new` is a node or a
942 list of nodes.
944 update = new
945 if not isinstance(new, Node):
946 # `new` is a list; update first child.
947 try:
948 update = new[0]
949 except IndexError:
950 update = None
951 if isinstance(update, Element):
952 update.update_basic_atts(self)
953 else:
954 # `update` is a Text node or `new` is an empty list.
955 # Assert that we aren't losing any attributes.
956 for att in self.basic_attributes:
957 assert not self[att], \
958 'Losing "%s" attribute: %s' % (att, self[att])
959 self.parent.replace(self, new)
961 def first_child_matching_class(self, childclass, start=0, end=sys.maxint):
963 Return the index of the first child whose class exactly matches.
965 Parameters:
967 - `childclass`: A `Node` subclass to search for, or a tuple of `Node`
968 classes. If a tuple, any of the classes may match.
969 - `start`: Initial index to check.
970 - `end`: Initial index to *not* check.
972 if not isinstance(childclass, tuple):
973 childclass = (childclass,)
974 for index in range(start, min(len(self), end)):
975 for c in childclass:
976 if isinstance(self[index], c):
977 return index
978 return None
980 def first_child_not_matching_class(self, childclass, start=0,
981 end=sys.maxint):
983 Return the index of the first child whose class does *not* match.
985 Parameters:
987 - `childclass`: A `Node` subclass to skip, or a tuple of `Node`
988 classes. If a tuple, none of the classes may match.
989 - `start`: Initial index to check.
990 - `end`: Initial index to *not* check.
992 if not isinstance(childclass, tuple):
993 childclass = (childclass,)
994 for index in range(start, min(len(self), end)):
995 for c in childclass:
996 if isinstance(self.children[index], c):
997 break
998 else:
999 return index
1000 return None
1002 def pformat(self, indent=' ', level=0):
1003 return ''.join(['%s%s\n' % (indent * level, self.starttag())] +
1004 [child.pformat(indent, level+1)
1005 for child in self.children])
1007 def copy(self):
1008 obj = self.__class__(rawsource=self.rawsource, **self.attributes)
1009 obj.document = self.document
1010 obj.source = self.source
1011 obj.line = self.line
1012 return obj
1014 def deepcopy(self):
1015 copy = self.copy()
1016 copy.extend([child.deepcopy() for child in self.children])
1017 return copy
1019 def set_class(self, name):
1020 """Add a new class to the "classes" attribute."""
1021 warnings.warn('docutils.nodes.Element.set_class deprecated; '
1022 "append to Element['classes'] list attribute directly",
1023 DeprecationWarning, stacklevel=2)
1024 assert ' ' not in name
1025 self['classes'].append(name.lower())
1027 def note_referenced_by(self, name=None, id=None):
1028 """Note that this Element has been referenced by its name
1029 `name` or id `id`."""
1030 self.referenced = 1
1031 # Element.expect_referenced_by_* dictionaries map names or ids
1032 # to nodes whose ``referenced`` attribute is set to true as
1033 # soon as this node is referenced by the given name or id.
1034 # Needed for target propagation.
1035 by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
1036 by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
1037 if by_name:
1038 assert name is not None
1039 by_name.referenced = 1
1040 if by_id:
1041 assert id is not None
1042 by_id.referenced = 1
1044 @classmethod
1045 def is_not_list_attribute(cls, attr):
1047 Returns True if and only if the given attribute is NOT one of the
1048 basic list attributes defined for all Elements.
1050 return attr not in cls.list_attributes
1052 @classmethod
1053 def is_not_known_attribute(cls, attr):
1055 Returns True if and only if the given attribute is NOT recognized by
1056 this class.
1058 return attr not in cls.known_attributes
1061 class TextElement(Element):
1064 An element which directly contains text.
1066 Its children are all `Text` or `Inline` subclass nodes. You can
1067 check whether an element's context is inline simply by checking whether
1068 its immediate parent is a `TextElement` instance (including subclasses).
1069 This is handy for nodes like `image` that can appear both inline and as
1070 standalone body elements.
1072 If passing children to `__init__()`, make sure to set `text` to
1073 ``''`` or some other suitable value.
1076 child_text_separator = ''
1077 """Separator for child nodes, used by `astext()` method."""
1079 def __init__(self, rawsource='', text='', *children, **attributes):
1080 if text != '':
1081 textnode = Text(text)
1082 Element.__init__(self, rawsource, textnode, *children,
1083 **attributes)
1084 else:
1085 Element.__init__(self, rawsource, *children, **attributes)
1088 class FixedTextElement(TextElement):
1090 """An element which directly contains preformatted text."""
1092 def __init__(self, rawsource='', text='', *children, **attributes):
1093 TextElement.__init__(self, rawsource, text, *children, **attributes)
1094 self.attributes['xml:space'] = 'preserve'
1097 # ========
1098 # Mixins
1099 # ========
1101 class Resolvable:
1103 resolved = 0
1106 class BackLinkable:
1108 def add_backref(self, refid):
1109 self['backrefs'].append(refid)
1112 # ====================
1113 # Element Categories
1114 # ====================
1116 class Root: pass
1118 class Titular: pass
1120 class PreBibliographic:
1121 """Category of Node which may occur before Bibliographic Nodes."""
1123 class Bibliographic: pass
1125 class Decorative(PreBibliographic): pass
1127 class Structural: pass
1129 class Body: pass
1131 class General(Body): pass
1133 class Sequential(Body):
1134 """List-like elements."""
1136 class Admonition(Body): pass
1138 class Special(Body):
1139 """Special internal body elements."""
1141 class Invisible(PreBibliographic):
1142 """Internal elements that don't appear in output."""
1144 class Part: pass
1146 class Inline: pass
1148 class Referential(Resolvable): pass
1151 class Targetable(Resolvable):
1153 referenced = 0
1155 indirect_reference_name = None
1156 """Holds the whitespace_normalized_name (contains mixed case) of a target.
1157 Required for MoinMoin/reST compatibility."""
1160 class Labeled:
1161 """Contains a `label` as its first element."""
1164 # ==============
1165 # Root Element
1166 # ==============
1168 class document(Root, Structural, Element):
1171 The document root element.
1173 Do not instantiate this class directly; use
1174 `docutils.utils.new_document()` instead.
1177 def __init__(self, settings, reporter, *args, **kwargs):
1178 Element.__init__(self, *args, **kwargs)
1180 self.current_source = None
1181 """Path to or description of the input source being processed."""
1183 self.current_line = None
1184 """Line number (1-based) of `current_source`."""
1186 self.settings = settings
1187 """Runtime settings data record."""
1189 self.reporter = reporter
1190 """System message generator."""
1192 self.indirect_targets = []
1193 """List of indirect target nodes."""
1195 self.substitution_defs = {}
1196 """Mapping of substitution names to substitution_definition nodes."""
1198 self.substitution_names = {}
1199 """Mapping of case-normalized substitution names to case-sensitive
1200 names."""
1202 self.refnames = {}
1203 """Mapping of names to lists of referencing nodes."""
1205 self.refids = {}
1206 """Mapping of ids to lists of referencing nodes."""
1208 self.nameids = {}
1209 """Mapping of names to unique id's."""
1211 self.nametypes = {}
1212 """Mapping of names to hyperlink type (boolean: True => explicit,
1213 False => implicit."""
1215 self.ids = {}
1216 """Mapping of ids to nodes."""
1218 self.footnote_refs = {}
1219 """Mapping of footnote labels to lists of footnote_reference nodes."""
1221 self.citation_refs = {}
1222 """Mapping of citation labels to lists of citation_reference nodes."""
1224 self.autofootnotes = []
1225 """List of auto-numbered footnote nodes."""
1227 self.autofootnote_refs = []
1228 """List of auto-numbered footnote_reference nodes."""
1230 self.symbol_footnotes = []
1231 """List of symbol footnote nodes."""
1233 self.symbol_footnote_refs = []
1234 """List of symbol footnote_reference nodes."""
1236 self.footnotes = []
1237 """List of manually-numbered footnote nodes."""
1239 self.citations = []
1240 """List of citation nodes."""
1242 self.autofootnote_start = 1
1243 """Initial auto-numbered footnote number."""
1245 self.symbol_footnote_start = 0
1246 """Initial symbol footnote symbol index."""
1248 self.id_start = 1
1249 """Initial ID number."""
1251 self.parse_messages = []
1252 """System messages generated while parsing."""
1254 self.transform_messages = []
1255 """System messages generated while applying transforms."""
1257 import docutils.transforms
1258 self.transformer = docutils.transforms.Transformer(self)
1259 """Storage for transforms to be applied to this document."""
1261 self.decoration = None
1262 """Document's `decoration` node."""
1264 self.document = self
1266 def __getstate__(self):
1268 Return dict with unpicklable references removed.
1270 state = self.__dict__.copy()
1271 state['reporter'] = None
1272 state['transformer'] = None
1273 return state
1275 def asdom(self, dom=None):
1276 """Return a DOM representation of this document."""
1277 if dom is None:
1278 import xml.dom.minidom as dom
1279 domroot = dom.Document()
1280 domroot.appendChild(self._dom_node(domroot))
1281 return domroot
1283 def set_id(self, node, msgnode=None):
1284 for id in node['ids']:
1285 if id in self.ids and self.ids[id] is not node:
1286 msg = self.reporter.severe('Duplicate ID: "%s".' % id)
1287 if msgnode != None:
1288 msgnode += msg
1289 if not node['ids']:
1290 for name in node['names']:
1291 id = self.settings.id_prefix + make_id(name)
1292 if id and id not in self.ids:
1293 break
1294 else:
1295 id = ''
1296 while not id or id in self.ids:
1297 id = (self.settings.id_prefix +
1298 self.settings.auto_id_prefix + str(self.id_start))
1299 self.id_start += 1
1300 node['ids'].append(id)
1301 self.ids[id] = node
1302 return id
1304 def set_name_id_map(self, node, id, msgnode=None, explicit=None):
1306 `self.nameids` maps names to IDs, while `self.nametypes` maps names to
1307 booleans representing hyperlink type (True==explicit,
1308 False==implicit). This method updates the mappings.
1310 The following state transition table shows how `self.nameids` ("ids")
1311 and `self.nametypes` ("types") change with new input (a call to this
1312 method), and what actions are performed ("implicit"-type system
1313 messages are INFO/1, and "explicit"-type system messages are ERROR/3):
1315 ==== ===== ======== ======== ======= ==== ===== =====
1316 Old State Input Action New State Notes
1317 ----------- -------- ----------------- ----------- -----
1318 ids types new type sys.msg. dupname ids types
1319 ==== ===== ======== ======== ======= ==== ===== =====
1320 - - explicit - - new True
1321 - - implicit - - new False
1322 None False explicit - - new True
1323 old False explicit implicit old new True
1324 None True explicit explicit new None True
1325 old True explicit explicit new,old None True [#]_
1326 None False implicit implicit new None False
1327 old False implicit implicit new,old None False
1328 None True implicit implicit new None True
1329 old True implicit implicit new old True
1330 ==== ===== ======== ======== ======= ==== ===== =====
1332 .. [#] Do not clear the name-to-id map or invalidate the old target if
1333 both old and new targets are external and refer to identical URIs.
1334 The new target is invalidated regardless.
1336 for name in node['names']:
1337 if name in self.nameids:
1338 self.set_duplicate_name_id(node, id, name, msgnode, explicit)
1339 else:
1340 self.nameids[name] = id
1341 self.nametypes[name] = explicit
1343 def set_duplicate_name_id(self, node, id, name, msgnode, explicit):
1344 old_id = self.nameids[name]
1345 old_explicit = self.nametypes[name]
1346 self.nametypes[name] = old_explicit or explicit
1347 if explicit:
1348 if old_explicit:
1349 level = 2
1350 if old_id is not None:
1351 old_node = self.ids[old_id]
1352 if 'refuri' in node:
1353 refuri = node['refuri']
1354 if old_node['names'] \
1355 and 'refuri' in old_node \
1356 and old_node['refuri'] == refuri:
1357 level = 1 # just inform if refuri's identical
1358 if level > 1:
1359 dupname(old_node, name)
1360 self.nameids[name] = None
1361 msg = self.reporter.system_message(
1362 level, 'Duplicate explicit target name: "%s".' % name,
1363 backrefs=[id], base_node=node)
1364 if msgnode != None:
1365 msgnode += msg
1366 dupname(node, name)
1367 else:
1368 self.nameids[name] = id
1369 if old_id is not None:
1370 old_node = self.ids[old_id]
1371 dupname(old_node, name)
1372 else:
1373 if old_id is not None and not old_explicit:
1374 self.nameids[name] = None
1375 old_node = self.ids[old_id]
1376 dupname(old_node, name)
1377 dupname(node, name)
1378 if not explicit or (not old_explicit and old_id is not None):
1379 msg = self.reporter.info(
1380 'Duplicate implicit target name: "%s".' % name,
1381 backrefs=[id], base_node=node)
1382 if msgnode != None:
1383 msgnode += msg
1385 def has_name(self, name):
1386 return name in self.nameids
1388 # "note" here is an imperative verb: "take note of".
1389 def note_implicit_target(self, target, msgnode=None):
1390 id = self.set_id(target, msgnode)
1391 self.set_name_id_map(target, id, msgnode, explicit=None)
1393 def note_explicit_target(self, target, msgnode=None):
1394 id = self.set_id(target, msgnode)
1395 self.set_name_id_map(target, id, msgnode, explicit=True)
1397 def note_refname(self, node):
1398 self.refnames.setdefault(node['refname'], []).append(node)
1400 def note_refid(self, node):
1401 self.refids.setdefault(node['refid'], []).append(node)
1403 def note_indirect_target(self, target):
1404 self.indirect_targets.append(target)
1405 if target['names']:
1406 self.note_refname(target)
1408 def note_anonymous_target(self, target):
1409 self.set_id(target)
1411 def note_autofootnote(self, footnote):
1412 self.set_id(footnote)
1413 self.autofootnotes.append(footnote)
1415 def note_autofootnote_ref(self, ref):
1416 self.set_id(ref)
1417 self.autofootnote_refs.append(ref)
1419 def note_symbol_footnote(self, footnote):
1420 self.set_id(footnote)
1421 self.symbol_footnotes.append(footnote)
1423 def note_symbol_footnote_ref(self, ref):
1424 self.set_id(ref)
1425 self.symbol_footnote_refs.append(ref)
1427 def note_footnote(self, footnote):
1428 self.set_id(footnote)
1429 self.footnotes.append(footnote)
1431 def note_footnote_ref(self, ref):
1432 self.set_id(ref)
1433 self.footnote_refs.setdefault(ref['refname'], []).append(ref)
1434 self.note_refname(ref)
1436 def note_citation(self, citation):
1437 self.citations.append(citation)
1439 def note_citation_ref(self, ref):
1440 self.set_id(ref)
1441 self.citation_refs.setdefault(ref['refname'], []).append(ref)
1442 self.note_refname(ref)
1444 def note_substitution_def(self, subdef, def_name, msgnode=None):
1445 name = whitespace_normalize_name(def_name)
1446 if name in self.substitution_defs:
1447 msg = self.reporter.error(
1448 'Duplicate substitution definition name: "%s".' % name,
1449 base_node=subdef)
1450 if msgnode != None:
1451 msgnode += msg
1452 oldnode = self.substitution_defs[name]
1453 dupname(oldnode, name)
1454 # keep only the last definition:
1455 self.substitution_defs[name] = subdef
1456 # case-insensitive mapping:
1457 self.substitution_names[fully_normalize_name(name)] = name
1459 def note_substitution_ref(self, subref, refname):
1460 subref['refname'] = whitespace_normalize_name(refname)
1462 def note_pending(self, pending, priority=None):
1463 self.transformer.add_pending(pending, priority)
1465 def note_parse_message(self, message):
1466 self.parse_messages.append(message)
1468 def note_transform_message(self, message):
1469 self.transform_messages.append(message)
1471 def note_source(self, source, offset):
1472 self.current_source = source
1473 if offset is None:
1474 self.current_line = offset
1475 else:
1476 self.current_line = offset + 1
1478 def copy(self):
1479 obj = self.__class__(self.settings, self.reporter,
1480 **self.attributes)
1481 obj.source = self.source
1482 obj.line = self.line
1483 return obj
1485 def get_decoration(self):
1486 if not self.decoration:
1487 self.decoration = decoration()
1488 index = self.first_child_not_matching_class(Titular)
1489 if index is None:
1490 self.append(self.decoration)
1491 else:
1492 self.insert(index, self.decoration)
1493 return self.decoration
1496 # ================
1497 # Title Elements
1498 # ================
1500 class title(Titular, PreBibliographic, TextElement): pass
1501 class subtitle(Titular, PreBibliographic, TextElement): pass
1502 class rubric(Titular, TextElement): pass
1505 # ========================
1506 # Bibliographic Elements
1507 # ========================
1509 class docinfo(Bibliographic, Element): pass
1510 class author(Bibliographic, TextElement): pass
1511 class authors(Bibliographic, Element): pass
1512 class organization(Bibliographic, TextElement): pass
1513 class address(Bibliographic, FixedTextElement): pass
1514 class contact(Bibliographic, TextElement): pass
1515 class version(Bibliographic, TextElement): pass
1516 class revision(Bibliographic, TextElement): pass
1517 class status(Bibliographic, TextElement): pass
1518 class date(Bibliographic, TextElement): pass
1519 class copyright(Bibliographic, TextElement): pass
1522 # =====================
1523 # Decorative Elements
1524 # =====================
1526 class decoration(Decorative, Element):
1528 def get_header(self):
1529 if not len(self.children) or not isinstance(self.children[0], header):
1530 self.insert(0, header())
1531 return self.children[0]
1533 def get_footer(self):
1534 if not len(self.children) or not isinstance(self.children[-1], footer):
1535 self.append(footer())
1536 return self.children[-1]
1539 class header(Decorative, Element): pass
1540 class footer(Decorative, Element): pass
1543 # =====================
1544 # Structural Elements
1545 # =====================
1547 class section(Structural, Element): pass
1550 class topic(Structural, Element):
1553 Topics are terminal, "leaf" mini-sections, like block quotes with titles,
1554 or textual figures. A topic is just like a section, except that it has no
1555 subsections, and it doesn't have to conform to section placement rules.
1557 Topics are allowed wherever body elements (list, table, etc.) are allowed,
1558 but only at the top level of a section or document. Topics cannot nest
1559 inside topics, sidebars, or body elements; you can't have a topic inside a
1560 table, list, block quote, etc.
1564 class sidebar(Structural, Element):
1567 Sidebars are like miniature, parallel documents that occur inside other
1568 documents, providing related or reference material. A sidebar is
1569 typically offset by a border and "floats" to the side of the page; the
1570 document's main text may flow around it. Sidebars can also be likened to
1571 super-footnotes; their content is outside of the flow of the document's
1572 main text.
1574 Sidebars are allowed wherever body elements (list, table, etc.) are
1575 allowed, but only at the top level of a section or document. Sidebars
1576 cannot nest inside sidebars, topics, or body elements; you can't have a
1577 sidebar inside a table, list, block quote, etc.
1581 class transition(Structural, Element): pass
1584 # ===============
1585 # Body Elements
1586 # ===============
1588 class paragraph(General, TextElement): pass
1589 class compound(General, Element): pass
1590 class container(General, Element): pass
1591 class bullet_list(Sequential, Element): pass
1592 class enumerated_list(Sequential, Element): pass
1593 class list_item(Part, Element): pass
1594 class definition_list(Sequential, Element): pass
1595 class definition_list_item(Part, Element): pass
1596 class term(Part, TextElement): pass
1597 class classifier(Part, TextElement): pass
1598 class definition(Part, Element): pass
1599 class field_list(Sequential, Element): pass
1600 class field(Part, Element): pass
1601 class field_name(Part, TextElement): pass
1602 class field_body(Part, Element): pass
1605 class option(Part, Element):
1607 child_text_separator = ''
1610 class option_argument(Part, TextElement):
1612 def astext(self):
1613 return self.get('delimiter', ' ') + TextElement.astext(self)
1616 class option_group(Part, Element):
1618 child_text_separator = ', '
1621 class option_list(Sequential, Element): pass
1624 class option_list_item(Part, Element):
1626 child_text_separator = ' '
1629 class option_string(Part, TextElement): pass
1630 class description(Part, Element): pass
1631 class literal_block(General, FixedTextElement): pass
1632 class doctest_block(General, FixedTextElement): pass
1633 class math_block(General, FixedTextElement): pass
1634 class line_block(General, Element): pass
1637 class line(Part, TextElement):
1639 indent = None
1642 class block_quote(General, Element): pass
1643 class attribution(Part, TextElement): pass
1644 class attention(Admonition, Element): pass
1645 class caution(Admonition, Element): pass
1646 class danger(Admonition, Element): pass
1647 class error(Admonition, Element): pass
1648 class important(Admonition, Element): pass
1649 class note(Admonition, Element): pass
1650 class tip(Admonition, Element): pass
1651 class hint(Admonition, Element): pass
1652 class warning(Admonition, Element): pass
1653 class admonition(Admonition, Element): pass
1654 class comment(Special, Invisible, FixedTextElement): pass
1655 class substitution_definition(Special, Invisible, TextElement): pass
1656 class target(Special, Invisible, Inline, TextElement, Targetable): pass
1657 class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
1658 class citation(General, BackLinkable, Element, Labeled, Targetable): pass
1659 class label(Part, TextElement): pass
1660 class figure(General, Element): pass
1661 class caption(Part, TextElement): pass
1662 class legend(Part, Element): pass
1663 class table(General, Element): pass
1664 class tgroup(Part, Element): pass
1665 class colspec(Part, Element): pass
1666 class thead(Part, Element): pass
1667 class tbody(Part, Element): pass
1668 class row(Part, Element): pass
1669 class entry(Part, Element): pass
1672 class system_message(Special, BackLinkable, PreBibliographic, Element):
1675 System message element.
1677 Do not instantiate this class directly; use
1678 ``document.reporter.info/warning/error/severe()`` instead.
1681 def __init__(self, message=None, *children, **attributes):
1682 rawsource = attributes.get('rawsource', '')
1683 if message:
1684 p = paragraph('', message)
1685 children = (p,) + children
1686 try:
1687 Element.__init__(self, rawsource, *children, **attributes)
1688 except:
1689 print 'system_message: children=%r' % (children,)
1690 raise
1692 def astext(self):
1693 line = self.get('line', '')
1694 return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
1695 self['level'], Element.astext(self))
1698 class pending(Special, Invisible, Element):
1701 The "pending" element is used to encapsulate a pending operation: the
1702 operation (transform), the point at which to apply it, and any data it
1703 requires. Only the pending operation's location within the document is
1704 stored in the public document tree (by the "pending" object itself); the
1705 operation and its data are stored in the "pending" object's internal
1706 instance attributes.
1708 For example, say you want a table of contents in your reStructuredText
1709 document. The easiest way to specify where to put it is from within the
1710 document, with a directive::
1712 .. contents::
1714 But the "contents" directive can't do its work until the entire document
1715 has been parsed and possibly transformed to some extent. So the directive
1716 code leaves a placeholder behind that will trigger the second phase of its
1717 processing, something like this::
1719 <pending ...public attributes...> + internal attributes
1721 Use `document.note_pending()` so that the
1722 `docutils.transforms.Transformer` stage of processing can run all pending
1723 transforms.
1726 def __init__(self, transform, details=None,
1727 rawsource='', *children, **attributes):
1728 Element.__init__(self, rawsource, *children, **attributes)
1730 self.transform = transform
1731 """The `docutils.transforms.Transform` class implementing the pending
1732 operation."""
1734 self.details = details or {}
1735 """Detail data (dictionary) required by the pending operation."""
1737 def pformat(self, indent=' ', level=0):
1738 internals = [
1739 '.. internal attributes:',
1740 ' .transform: %s.%s' % (self.transform.__module__,
1741 self.transform.__name__),
1742 ' .details:']
1743 details = self.details.items()
1744 details.sort()
1745 for key, value in details:
1746 if isinstance(value, Node):
1747 internals.append('%7s%s:' % ('', key))
1748 internals.extend(['%9s%s' % ('', line)
1749 for line in value.pformat().splitlines()])
1750 elif value and isinstance(value, list) \
1751 and isinstance(value[0], Node):
1752 internals.append('%7s%s:' % ('', key))
1753 for v in value:
1754 internals.extend(['%9s%s' % ('', line)
1755 for line in v.pformat().splitlines()])
1756 else:
1757 internals.append('%7s%s: %r' % ('', key, value))
1758 return (Element.pformat(self, indent, level)
1759 + ''.join([(' %s%s\n' % (indent * level, line))
1760 for line in internals]))
1762 def copy(self):
1763 obj = self.__class__(self.transform, self.details, self.rawsource,
1764 **self.attributes)
1765 obj.document = self.document
1766 obj.source = self.source
1767 obj.line = self.line
1768 return obj
1771 class raw(Special, Inline, PreBibliographic, FixedTextElement):
1774 Raw data that is to be passed untouched to the Writer.
1777 pass
1780 # =================
1781 # Inline Elements
1782 # =================
1784 class emphasis(Inline, TextElement): pass
1785 class strong(Inline, TextElement): pass
1786 class literal(Inline, TextElement): pass
1787 class reference(General, Inline, Referential, TextElement): pass
1788 class footnote_reference(Inline, Referential, TextElement): pass
1789 class citation_reference(Inline, Referential, TextElement): pass
1790 class substitution_reference(Inline, TextElement): pass
1791 class title_reference(Inline, TextElement): pass
1792 class abbreviation(Inline, TextElement): pass
1793 class acronym(Inline, TextElement): pass
1794 class superscript(Inline, TextElement): pass
1795 class subscript(Inline, TextElement): pass
1796 class math(Inline, TextElement): pass
1799 class image(General, Inline, Element):
1801 def astext(self):
1802 return self.get('alt', '')
1805 class inline(Inline, TextElement): pass
1806 class problematic(Inline, TextElement): pass
1807 class generated(Inline, TextElement): pass
1810 # ========================================
1811 # Auxiliary Classes, Functions, and Data
1812 # ========================================
1814 node_class_names = """
1815 Text
1816 abbreviation acronym address admonition attention attribution author
1817 authors
1818 block_quote bullet_list
1819 caption caution citation citation_reference classifier colspec comment
1820 compound contact container copyright
1821 danger date decoration definition definition_list definition_list_item
1822 description docinfo doctest_block document
1823 emphasis entry enumerated_list error
1824 field field_body field_list field_name figure footer
1825 footnote footnote_reference
1826 generated
1827 header hint
1828 image important inline
1829 label legend line line_block list_item literal literal_block
1830 math math_block
1831 note
1832 option option_argument option_group option_list option_list_item
1833 option_string organization
1834 paragraph pending problematic
1835 raw reference revision row rubric
1836 section sidebar status strong subscript substitution_definition
1837 substitution_reference subtitle superscript system_message
1838 table target tbody term tgroup thead tip title title_reference topic
1839 transition
1840 version
1841 warning""".split()
1842 """A list of names of all concrete Node subclasses."""
1845 class NodeVisitor:
1848 "Visitor" pattern [GoF95]_ abstract superclass implementation for
1849 document tree traversals.
1851 Each node class has corresponding methods, doing nothing by
1852 default; override individual methods for specific and useful
1853 behaviour. The `dispatch_visit()` method is called by
1854 `Node.walk()` upon entering a node. `Node.walkabout()` also calls
1855 the `dispatch_departure()` method before exiting a node.
1857 The dispatch methods call "``visit_`` + node class name" or
1858 "``depart_`` + node class name", resp.
1860 This is a base class for visitors whose ``visit_...`` & ``depart_...``
1861 methods should be implemented for *all* node types encountered (such as
1862 for `docutils.writers.Writer` subclasses). Unimplemented methods will
1863 raise exceptions.
1865 For sparse traversals, where only certain node types are of interest,
1866 subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform
1867 processing is desired, subclass `GenericNodeVisitor`.
1869 .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
1870 Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
1871 1995.
1874 optional = ()
1876 Tuple containing node class names (as strings).
1878 No exception will be raised if writers do not implement visit
1879 or departure functions for these node classes.
1881 Used to ensure transitional compatibility with existing 3rd-party writers.
1884 def __init__(self, document):
1885 self.document = document
1887 def dispatch_visit(self, node):
1889 Call self."``visit_`` + node class name" with `node` as
1890 parameter. If the ``visit_...`` method does not exist, call
1891 self.unknown_visit.
1893 node_name = node.__class__.__name__
1894 method = getattr(self, 'visit_' + node_name, self.unknown_visit)
1895 self.document.reporter.debug(
1896 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
1897 % (method.__name__, node_name))
1898 return method(node)
1900 def dispatch_departure(self, node):
1902 Call self."``depart_`` + node class name" with `node` as
1903 parameter. If the ``depart_...`` method does not exist, call
1904 self.unknown_departure.
1906 node_name = node.__class__.__name__
1907 method = getattr(self, 'depart_' + node_name, self.unknown_departure)
1908 self.document.reporter.debug(
1909 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
1910 % (method.__name__, node_name))
1911 return method(node)
1913 def unknown_visit(self, node):
1915 Called when entering unknown `Node` types.
1917 Raise an exception unless overridden.
1919 if (self.document.settings.strict_visitor
1920 or node.__class__.__name__ not in self.optional):
1921 raise NotImplementedError(
1922 '%s visiting unknown node type: %s'
1923 % (self.__class__, node.__class__.__name__))
1925 def unknown_departure(self, node):
1927 Called before exiting unknown `Node` types.
1929 Raise exception unless overridden.
1931 if (self.document.settings.strict_visitor
1932 or node.__class__.__name__ not in self.optional):
1933 raise NotImplementedError(
1934 '%s departing unknown node type: %s'
1935 % (self.__class__, node.__class__.__name__))
1938 class SparseNodeVisitor(NodeVisitor):
1941 Base class for sparse traversals, where only certain node types are of
1942 interest. When ``visit_...`` & ``depart_...`` methods should be
1943 implemented for *all* node types (such as for `docutils.writers.Writer`
1944 subclasses), subclass `NodeVisitor` instead.
1948 class GenericNodeVisitor(NodeVisitor):
1951 Generic "Visitor" abstract superclass, for simple traversals.
1953 Unless overridden, each ``visit_...`` method calls `default_visit()`, and
1954 each ``depart_...`` method (when using `Node.walkabout()`) calls
1955 `default_departure()`. `default_visit()` (and `default_departure()`) must
1956 be overridden in subclasses.
1958 Define fully generic visitors by overriding `default_visit()` (and
1959 `default_departure()`) only. Define semi-generic visitors by overriding
1960 individual ``visit_...()`` (and ``depart_...()``) methods also.
1962 `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should
1963 be overridden for default behavior.
1966 def default_visit(self, node):
1967 """Override for generic, uniform traversals."""
1968 raise NotImplementedError
1970 def default_departure(self, node):
1971 """Override for generic, uniform traversals."""
1972 raise NotImplementedError
1974 def _call_default_visit(self, node):
1975 self.default_visit(node)
1977 def _call_default_departure(self, node):
1978 self.default_departure(node)
1980 def _nop(self, node):
1981 pass
1983 def _add_node_class_names(names):
1984 """Save typing with dynamic assignments:"""
1985 for _name in names:
1986 setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
1987 setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
1988 setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
1989 setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
1991 _add_node_class_names(node_class_names)
1994 class TreeCopyVisitor(GenericNodeVisitor):
1997 Make a complete copy of a tree or branch, including element attributes.
2000 def __init__(self, document):
2001 GenericNodeVisitor.__init__(self, document)
2002 self.parent_stack = []
2003 self.parent = []
2005 def get_tree_copy(self):
2006 return self.parent[0]
2008 def default_visit(self, node):
2009 """Copy the current node, and make it the new acting parent."""
2010 newnode = node.copy()
2011 self.parent.append(newnode)
2012 self.parent_stack.append(self.parent)
2013 self.parent = newnode
2015 def default_departure(self, node):
2016 """Restore the previous acting parent."""
2017 self.parent = self.parent_stack.pop()
2020 class TreePruningException(Exception):
2023 Base class for `NodeVisitor`-related tree pruning exceptions.
2025 Raise subclasses from within ``visit_...`` or ``depart_...`` methods
2026 called from `Node.walk()` and `Node.walkabout()` tree traversals to prune
2027 the tree traversed.
2030 pass
2033 class SkipChildren(TreePruningException):
2036 Do not visit any children of the current node. The current node's
2037 siblings and ``depart_...`` method are not affected.
2040 pass
2043 class SkipSiblings(TreePruningException):
2046 Do not visit any more siblings (to the right) of the current node. The
2047 current node's children and its ``depart_...`` method are not affected.
2050 pass
2053 class SkipNode(TreePruningException):
2056 Do not visit the current node's children, and do not call the current
2057 node's ``depart_...`` method.
2060 pass
2063 class SkipDeparture(TreePruningException):
2066 Do not call the current node's ``depart_...`` method. The current node's
2067 children and siblings are not affected.
2070 pass
2073 class NodeFound(TreePruningException):
2076 Raise to indicate that the target of a search has been found. This
2077 exception must be caught by the client; it is not caught by the traversal
2078 code.
2081 pass
2084 class StopTraversal(TreePruningException):
2087 Stop the traversal alltogether. The current node's ``depart_...`` method
2088 is not affected. The parent nodes ``depart_...`` methods are also called
2089 as usual. No other nodes are visited. This is an alternative to
2090 NodeFound that does not cause exception handling to trickle up to the
2091 caller.
2094 pass
2097 def make_id(string):
2099 Convert `string` into an identifier and return it.
2101 Docutils identifiers will conform to the regular expression
2102 ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class"
2103 and "id" attributes) should have no underscores, colons, or periods.
2104 Hyphens may be used.
2106 - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
2108 ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
2109 followed by any number of letters, digits ([0-9]), hyphens ("-"),
2110 underscores ("_"), colons (":"), and periods (".").
2112 - However the `CSS1 spec`_ defines identifiers based on the "name" token,
2113 a tighter interpretation ("flex" tokenizer notation; "latin1" and
2114 "escape" 8-bit characters have been replaced with entities)::
2116 unicode \\[0-9a-f]{1,4}
2117 latin1 [&iexcl;-&yuml;]
2118 escape {unicode}|\\[ -~&iexcl;-&yuml;]
2119 nmchar [-a-z0-9]|{latin1}|{escape}
2120 name {nmchar}+
2122 The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"),
2123 or periods ("."), therefore "class" and "id" attributes should not contain
2124 these characters. They should be replaced with hyphens ("-"). Combined
2125 with HTML's requirements (the first character must be a letter; no
2126 "unicode", "latin1", or "escape" characters), this results in the
2127 ``[a-z](-?[a-z0-9]+)*`` pattern.
2129 .. _HTML 4.01 spec: http://www.w3.org/TR/html401
2130 .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
2132 id = string.lower()
2133 if not isinstance(id, unicode):
2134 id = id.decode()
2135 id = id.translate(_non_id_translate_digraphs)
2136 id = id.translate(_non_id_translate)
2137 # get rid of non-ascii characters.
2138 # 'ascii' lowercase to prevent problems with turkish locale.
2139 id = unicodedata.normalize('NFKD', id).\
2140 encode('ascii', 'ignore').decode('ascii')
2141 # shrink runs of whitespace and replace by hyphen
2142 id = _non_id_chars.sub('-', ' '.join(id.split()))
2143 id = _non_id_at_ends.sub('', id)
2144 return str(id)
2146 _non_id_chars = re.compile('[^a-z0-9]+')
2147 _non_id_at_ends = re.compile('^[-0-9]+|-+$')
2148 _non_id_translate = {
2149 0x00f8: u'o', # o with stroke
2150 0x0111: u'd', # d with stroke
2151 0x0127: u'h', # h with stroke
2152 0x0131: u'i', # dotless i
2153 0x0142: u'l', # l with stroke
2154 0x0167: u't', # t with stroke
2155 0x0180: u'b', # b with stroke
2156 0x0183: u'b', # b with topbar
2157 0x0188: u'c', # c with hook
2158 0x018c: u'd', # d with topbar
2159 0x0192: u'f', # f with hook
2160 0x0199: u'k', # k with hook
2161 0x019a: u'l', # l with bar
2162 0x019e: u'n', # n with long right leg
2163 0x01a5: u'p', # p with hook
2164 0x01ab: u't', # t with palatal hook
2165 0x01ad: u't', # t with hook
2166 0x01b4: u'y', # y with hook
2167 0x01b6: u'z', # z with stroke
2168 0x01e5: u'g', # g with stroke
2169 0x0225: u'z', # z with hook
2170 0x0234: u'l', # l with curl
2171 0x0235: u'n', # n with curl
2172 0x0236: u't', # t with curl
2173 0x0237: u'j', # dotless j
2174 0x023c: u'c', # c with stroke
2175 0x023f: u's', # s with swash tail
2176 0x0240: u'z', # z with swash tail
2177 0x0247: u'e', # e with stroke
2178 0x0249: u'j', # j with stroke
2179 0x024b: u'q', # q with hook tail
2180 0x024d: u'r', # r with stroke
2181 0x024f: u'y', # y with stroke
2183 _non_id_translate_digraphs = {
2184 0x00df: u'sz', # ligature sz
2185 0x00e6: u'ae', # ae
2186 0x0153: u'oe', # ligature oe
2187 0x0238: u'db', # db digraph
2188 0x0239: u'qp', # qp digraph
2191 def dupname(node, name):
2192 node['dupnames'].append(name)
2193 node['names'].remove(name)
2194 # Assume that this method is referenced, even though it isn't; we
2195 # don't want to throw unnecessary system_messages.
2196 node.referenced = 1
2198 def fully_normalize_name(name):
2199 """Return a case- and whitespace-normalized name."""
2200 return ' '.join(name.lower().split())
2202 def whitespace_normalize_name(name):
2203 """Return a whitespace-normalized name."""
2204 return ' '.join(name.split())
2206 def serial_escape(value):
2207 """Escape string values that are elements of a list, for serialization."""
2208 return value.replace('\\', r'\\').replace(' ', r'\ ')
2210 def pseudo_quoteattr(value):
2211 """Quote attributes for pseudo-xml"""
2212 return '"%s"' % value
2214 # \f
2216 # Local Variables:
2217 # indent-tabs-mode: nil
2218 # sentence-end-double-space: t
2219 # fill-column: 78
2220 # End: