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.
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
20 .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
23 __docformat__
= 'reStructuredText'
32 # ==============================
33 # Functional Node Base Classes
34 # ==============================
38 """Abstract base class of nodes in a document tree."""
41 """Back-reference to the Node immediately containing this Node."""
44 """The `document` node at the root of the tree containing this Node."""
47 """Path or description of the input source which generated this Node."""
50 """The line number (1-based) of the beginning of this Node in `source`."""
52 def __nonzero__(self
):
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
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
67 return unicode(self
).encode('raw_unicode_escape')
69 def asdom(self
, dom
=None):
70 """Return a DOM **fragment** representation of this Node."""
72 import xml
.dom
.minidom
as dom
73 domroot
= dom
.Document()
74 return self
._dom
_node
(domroot
)
76 def pformat(self
, indent
=' ', level
=0):
78 Return an indented pseudo-XML representation, for test purposes.
80 Override in subclasses.
82 raise NotImplementedError
85 """Return a copy of self."""
86 raise NotImplementedError
89 """Return a deep copy of self (also copying children)."""
90 raise NotImplementedError
92 def setup_child(self
, child
):
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
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.
125 visitor
.document
.reporter
.debug(
126 'docutils.nodes.Node.walk calling dispatch_visit for %s'
127 % self
.__class
__.__name
__)
130 visitor
.dispatch_visit(self
)
131 except (SkipChildren
, SkipNode
):
133 except SkipDeparture
: # not applicable; ignore
135 children
= self
.children
137 for child
in children
[:]:
138 if child
.walk(visitor
):
143 except StopTraversal
:
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.
161 visitor
.document
.reporter
.debug(
162 'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
163 % self
.__class
__.__name
__)
166 visitor
.dispatch_visit(self
)
169 except SkipDeparture
:
171 children
= self
.children
173 for child
in children
[:]:
174 if child
.walkabout(visitor
):
181 except StopTraversal
:
184 visitor
.document
.reporter
.debug(
185 'docutils.nodes.Node.walkabout calling dispatch_departure '
186 'for %s' % self
.__class
__.__name
__)
187 visitor
.dispatch_departure(self
)
190 def _fast_traverse(self
, cls
):
191 """Specialized traverse() that only supports instance checks."""
193 if isinstance(self
, cls
):
195 for child
in self
.children
:
196 result
.extend(child
._fast
_traverse
(cls
))
199 def _all_traverse(self
):
200 """Specialized traverse() that doesn't check for a condition."""
203 for child
in self
.children
:
204 result
.extend(child
._all
_traverse
())
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::
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>]
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
)
260 if include_self
and (condition
is None or condition(self
)):
262 if descend
and len(self
.children
):
264 r
.extend(child
.traverse(include_self
=True, descend
=True,
265 siblings
=False, ascend
=False,
266 condition
=condition
))
267 if siblings
or ascend
:
270 index
= node
.parent
.index(node
)
271 for sibling
in node
.parent
[index
+1:]:
272 r
.extend(sibling
.traverse(include_self
=True,
274 siblings
=False, ascend
=False,
275 condition
=condition
))
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
)
299 if sys
.version_info
< (3,):
300 class reprunicode(unicode):
302 A unicode sub-class that removes the initial u from unicode's repr.
306 return unicode.__repr
__(self
)[1:]
308 reprunicode
= unicode
313 Failsave conversion of `unicode` to `str`.
315 if sys
.version_info
< (3,) and isinstance(s
, unicode):
316 return s
.encode('ascii', 'backslashreplace')
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.
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
)
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):
350 if len(data
) > maxlen
:
351 data
= data
[:maxlen
-4] + ' ...'
352 return '<%s: %r>' % (self
.tagname
, reprunicode(data
))
355 return self
.shortrepr(maxlen
=68)
357 def _dom_node(self
, domroot
):
358 return domroot
.createTextNode(unicode(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
373 return self
.__class
__(reprunicode(self
), rawsource
=self
.rawsource
)
378 def pformat(self
, indent
=' ', level
=0):
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')+'\\')
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
'',
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::
421 Elements may be constructed using the ``+=`` operator. To add one new
422 child node to element, do::
426 This is equivalent to ``element.append(node)``.
428 To add a list of multiple child nodes at once, use the same ``+=``
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
451 known_attributes
= list_attributes
+ ('source', 'rawsource')
452 """List attributes that are known to the Element base class."""
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 '').
469 """List of child nodes (elements and/or `Text`)."""
471 self
.extend(children
) # maintain parent info
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():
482 if att
in self
.list_attributes
:
483 # mutable list; make a copy for this node
484 self
.attributes
[att
] = value
[:]
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
))
503 for c
in self
.children
:
504 data
+= c
.shortrepr()
506 data
= data
[:56] + ' ...'
509 return '<%s "%s": %s>' % (self
.__class
__.__name
__,
510 '; '.join([ensure_str(n
) for n
in self
['names']]), data
)
512 return '<%s: %s>' % (self
.__class
__.__name
__, data
)
516 return '<%s "%s"...>' % (self
.__class
__.__name
__,
517 '; '.join([ensure_str(n
) for n
in self
['names']]))
519 return '<%s...>' % self
.tagname
521 def __unicode__(self
):
523 return u
'%s%s%s' % (self
.starttag(),
524 ''.join([unicode(c
) for c
in self
.children
]),
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
)
542 if isinstance(value
, list):
543 values
= [serial_escape('%s' % (v
,)) for v
in value
]
544 value
= ' '.join(values
)
546 value
= unicode(value
)
547 value
= quoteattr(value
)
548 parts
.append(u
'%s=%s' % (name
, value
))
549 return u
'<%s>' % u
' '.join(parts
)
552 return '</%s>' % self
.tagname
555 return u
'<%s/>' % u
' '.join([self
.tagname
] +
557 for n
, v
in self
.attlist()])
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
]
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'
590 self
.setup_child(node
)
591 self
.children
[key
.start
:key
.stop
] = item
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
]
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
):
618 elif other
is not None:
623 return self
.child_text_separator
.join(
624 [child
.astext() for child
in self
.children
])
626 def non_default_attributes(self
):
628 for key
, value
in self
.attributes
.items():
629 if self
.is_not_default(key
):
634 attlist
= self
.non_default_attributes().items()
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
)
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-'):
667 return self
.parent
.get_language(fallback
)
668 except AttributeError:
671 def append(self
, item
):
672 self
.setup_child(item
)
673 self
.children
.append(item
)
675 def extend(self
, item
):
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
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
:
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.
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.
733 if not isinstance(self
.get(attr
), list):
734 self
[attr
] = [self
[attr
]]
735 if not isinstance(value
, list):
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.
745 if force
or self
.get(attr
) is None:
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
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
)
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
)
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?
824 filter_fun
= self
.is_not_list_attribute
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,
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
,
857 def update_all_atts_concatenating(self
, dict_
, replace
= True,
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
,
881 def update_all_atts_coercion(self
, dict_
, replace
= True,
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
,
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
)
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
)
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
945 if not isinstance(new
, Node
):
946 # `new` is a list; update first child.
951 if isinstance(update
, Element
):
952 update
.update_basic_atts(self
)
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.
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
)):
976 if isinstance(self
[index
], c
):
980 def first_child_not_matching_class(self
, childclass
, start
=0,
983 Return the index of the first child whose class does *not* match.
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
)):
996 if isinstance(self
.children
[index
], c
):
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
])
1008 obj
= self
.__class
__(rawsource
=self
.rawsource
, **self
.attributes
)
1009 obj
.document
= self
.document
1010 obj
.source
= self
.source
1011 obj
.line
= self
.line
1016 copy
.extend([child
.deepcopy() for child
in self
.children
])
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`."""
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)
1038 assert name
is not None
1039 by_name
.referenced
= 1
1041 assert id is not None
1042 by_id
.referenced
= 1
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
1053 def is_not_known_attribute(cls
, attr
):
1055 Returns True if and only if the given attribute is NOT recognized by
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
):
1081 textnode
= Text(text
)
1082 Element
.__init
__(self
, rawsource
, textnode
, *children
,
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'
1108 def add_backref(self
, refid
):
1109 self
['backrefs'].append(refid
)
1112 # ====================
1113 # Element Categories
1114 # ====================
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
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."""
1148 class Referential(Resolvable
): pass
1151 class Targetable(Resolvable
):
1155 indirect_reference_name
= None
1156 """Holds the whitespace_normalized_name (contains mixed case) of a target.
1157 Required for MoinMoin/reST compatibility."""
1161 """Contains a `label` as its first element."""
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
1203 """Mapping of names to lists of referencing nodes."""
1206 """Mapping of ids to lists of referencing nodes."""
1209 """Mapping of names to unique id's."""
1212 """Mapping of names to hyperlink type (boolean: True => explicit,
1213 False => implicit."""
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."""
1237 """List of manually-numbered footnote nodes."""
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."""
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
1275 def asdom(self
, dom
=None):
1276 """Return a DOM representation of this document."""
1278 import xml
.dom
.minidom
as dom
1279 domroot
= dom
.Document()
1280 domroot
.appendChild(self
._dom
_node
(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)
1290 for name
in node
['names']:
1291 id = self
.settings
.id_prefix
+ make_id(name
)
1292 if id and id not in self
.ids
:
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
))
1300 node
['ids'].append(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
)
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
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
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
)
1368 self
.nameids
[name
] = id
1369 if old_id
is not None:
1370 old_node
= self
.ids
[old_id
]
1371 dupname(old_node
, name
)
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
)
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
)
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
)
1406 self
.note_refname(target
)
1408 def note_anonymous_target(self
, target
):
1411 def note_autofootnote(self
, footnote
):
1412 self
.set_id(footnote
)
1413 self
.autofootnotes
.append(footnote
)
1415 def note_autofootnote_ref(self
, 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
):
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
):
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
):
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
,
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
1474 self
.current_line
= offset
1476 self
.current_line
= offset
+ 1
1479 obj
= self
.__class
__(self
.settings
, self
.reporter
,
1481 obj
.source
= self
.source
1482 obj
.line
= self
.line
1485 def get_decoration(self
):
1486 if not self
.decoration
:
1487 self
.decoration
= decoration()
1488 index
= self
.first_child_not_matching_class(Titular
)
1490 self
.append(self
.decoration
)
1492 self
.insert(index
, self
.decoration
)
1493 return self
.decoration
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
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
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
):
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
):
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', '')
1684 p
= paragraph('', message
)
1685 children
= (p
,) + children
1687 Element
.__init
__(self
, rawsource
, *children
, **attributes
)
1689 print 'system_message: children=%r' % (children
,)
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::
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
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
1734 self
.details
= details
or {}
1735 """Detail data (dictionary) required by the pending operation."""
1737 def pformat(self
, indent
=' ', level
=0):
1739 '.. internal attributes:',
1740 ' .transform: %s.%s' % (self
.transform
.__module
__,
1741 self
.transform
.__name
__),
1743 details
= self
.details
.items()
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
))
1754 internals
.extend(['%9s%s' % ('', line
)
1755 for line
in v
.pformat().splitlines()])
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
]))
1763 obj
= self
.__class
__(self
.transform
, self
.details
, self
.rawsource
,
1765 obj
.document
= self
.document
1766 obj
.source
= self
.source
1767 obj
.line
= self
.line
1771 class raw(Special
, Inline
, PreBibliographic
, FixedTextElement
):
1774 Raw data that is to be passed untouched to the Writer.
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
):
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
= """
1816 abbreviation acronym address admonition attention attribution author
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
1828 image important inline
1829 label legend line line_block list_item literal literal_block
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
1842 """A list of names of all concrete Node subclasses."""
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
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,
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
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
))
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
))
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
):
1983 def _add_node_class_names(names
):
1984 """Save typing with dynamic assignments:"""
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
= []
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
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.
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.
2053 class SkipNode(TreePruningException
):
2056 Do not visit the current node's children, and do not call the current
2057 node's ``depart_...`` method.
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.
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
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
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 [¡-ÿ]
2118 escape {unicode}|\\[ -~¡-ÿ]
2119 nmchar [-a-z0-9]|{latin1}|{escape}
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
2133 if not isinstance(id, unicode):
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)
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
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.
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
2217 # indent-tabs-mode: nil
2218 # sentence-end-double-space: t