latex2e writer : Move usepackage hyperref after stylesheet inclusion.
[docutils.git] / docutils / nodes.py
blobffb679b17249ebf553d1def62e736474bdd8ba0e
1 # $Id$
2 # Author: David Goodger <goodger@python.org>
3 # Copyright: This module has been placed in the public domain.
5 """
6 Docutils document tree element class library.
8 Classes in CamelCase are abstract base classes or auxiliary classes. The one
9 exception is `Text`, for a text (PCDATA) node; uppercase is used to
10 differentiate from element classes. Classes in lower_case_with_underscores
11 are element classes, matching the XML element generic identifiers in the DTD_.
13 The position of each node (the level at which it can occur) is significant and
14 is represented by abstract base classes (`Root`, `Structural`, `Body`,
15 `Inline`, etc.). Certain transformations will be easier because we can use
16 ``isinstance(node, base_class)`` to determine the position of the node in the
17 hierarchy.
19 .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
20 """
22 __docformat__ = 'reStructuredText'
24 import sys
25 import os
26 import re
27 import warnings
28 from types import IntType, SliceType, StringType, UnicodeType, \
29 TupleType, ListType, ClassType, TypeType
30 from UserString import UserString
33 # ==============================
34 # Functional Node Base Classes
35 # ==============================
37 class Node:
39 """Abstract base class of nodes in a document tree."""
41 parent = None
42 """Back-reference to the Node immediately containing this Node."""
44 document = None
45 """The `document` node at the root of the tree containing this Node."""
47 source = None
48 """Path or description of the input source which generated this Node."""
50 line = None
51 """The line number (1-based) of the beginning of this Node in `source`."""
53 def __nonzero__(self):
54 """
55 Node instances are always true, even if they're empty. A node is more
56 than a simple container. Its boolean "truth" does not depend on
57 having one or more subnodes in the doctree.
59 Use `len()` to check node length. Use `None` to represent a boolean
60 false value.
61 """
62 return 1
64 def __str__(self):
65 return self.__unicode__().encode('raw_unicode_escape')
67 def __unicode__(self):
68 # Override in subclass.
69 raise NotImplementedError
71 def asdom(self, dom=None):
72 """Return a DOM **fragment** representation of this Node."""
73 if dom is None:
74 import xml.dom.minidom as dom
75 domroot = dom.Document()
76 return self._dom_node(domroot)
78 def pformat(self, indent=' ', level=0):
79 """
80 Return an indented pseudo-XML representation, for test purposes.
82 Override in subclasses.
83 """
84 raise NotImplementedError
86 def copy(self):
87 """Return a copy of self."""
88 raise NotImplementedError
90 def deepcopy(self):
91 """Return a deep copy of self (also copying children)."""
92 raise NotImplementedError
94 def setup_child(self, child):
95 child.parent = self
96 if self.document:
97 child.document = self.document
98 if child.source is None:
99 child.source = self.document.current_source
100 if child.line is None:
101 child.line = self.document.current_line
103 def walk(self, visitor):
105 Traverse a tree of `Node` objects, calling the
106 `dispatch_visit()` method of `visitor` when entering each
107 node. (The `walkabout()` method is similar, except it also
108 calls the `dispatch_departure()` method before exiting each
109 node.)
111 This tree traversal supports limited in-place tree
112 modifications. Replacing one node with one or more nodes is
113 OK, as is removing an element. However, if the node removed
114 or replaced occurs after the current node, the old node will
115 still be traversed, and any new nodes will not.
117 Within ``visit`` methods (and ``depart`` methods for
118 `walkabout()`), `TreePruningException` subclasses may be raised
119 (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
121 Parameter `visitor`: A `NodeVisitor` object, containing a
122 ``visit`` implementation for each `Node` subclass encountered.
124 Return true if we should stop the traversal.
126 stop = 0
127 visitor.document.reporter.debug(
128 'docutils.nodes.Node.walk calling dispatch_visit for %s'
129 % self.__class__.__name__)
130 try:
131 try:
132 visitor.dispatch_visit(self)
133 except (SkipChildren, SkipNode):
134 return stop
135 except SkipDeparture: # not applicable; ignore
136 pass
137 children = self.children
138 try:
139 for child in children[:]:
140 if child.walk(visitor):
141 stop = 1
142 break
143 except SkipSiblings:
144 pass
145 except StopTraversal:
146 stop = 1
147 return stop
149 def walkabout(self, visitor):
151 Perform a tree traversal similarly to `Node.walk()` (which
152 see), except also call the `dispatch_departure()` method
153 before exiting each node.
155 Parameter `visitor`: A `NodeVisitor` object, containing a
156 ``visit`` and ``depart`` implementation for each `Node`
157 subclass encountered.
159 Return true if we should stop the traversal.
161 call_depart = 1
162 stop = 0
163 visitor.document.reporter.debug(
164 'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
165 % self.__class__.__name__)
166 try:
167 try:
168 visitor.dispatch_visit(self)
169 except SkipNode:
170 return stop
171 except SkipDeparture:
172 call_depart = 0
173 children = self.children
174 try:
175 for child in children[:]:
176 if child.walkabout(visitor):
177 stop = 1
178 break
179 except SkipSiblings:
180 pass
181 except SkipChildren:
182 pass
183 except StopTraversal:
184 stop = 1
185 if call_depart:
186 visitor.document.reporter.debug(
187 'docutils.nodes.Node.walkabout calling dispatch_departure '
188 'for %s' % self.__class__.__name__)
189 visitor.dispatch_departure(self)
190 return stop
192 def traverse(self, condition=None,
193 include_self=1, descend=1, siblings=0, ascend=0):
195 Return an iterable containing
197 * self (if include_self is true)
198 * all descendants in tree traversal order (if descend is true)
199 * all siblings (if siblings is true) and their descendants (if
200 also descend is true)
201 * the siblings of the parent (if ascend is true) and their
202 descendants (if also descend is true), and so on
204 If `condition` is not None, the iterable contains only nodes
205 for which ``condition(node)`` is true. If `condition` is a
206 node class ``cls``, it is equivalent to a function consisting
207 of ``return isinstance(node, cls)``.
209 If ascend is true, assume siblings to be true as well.
211 For example, given the following tree::
213 <paragraph>
214 <emphasis> <--- emphasis.traverse() and
215 <strong> <--- strong.traverse() are called.
218 <reference name="Baz" refid="baz">
221 Then list(emphasis.traverse()) equals ::
223 [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
225 and list(strong.traverse(ascend=1)) equals ::
227 [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
229 r = []
230 if ascend:
231 siblings=1
232 # Check if `condition` is a class (check for TypeType for Python
233 # implementations that use only new-style classes, like PyPy).
234 if isinstance(condition, (ClassType, TypeType)):
235 node_class = condition
236 def condition(node, node_class=node_class):
237 return isinstance(node, node_class)
238 if include_self and (condition is None or condition(self)):
239 r.append(self)
240 if descend and len(self.children):
241 for child in self:
242 r.extend(child.traverse(
243 include_self=1, descend=1, siblings=0, ascend=0,
244 condition=condition))
245 if siblings or ascend:
246 node = self
247 while node.parent:
248 index = node.parent.index(node)
249 for sibling in node.parent[index+1:]:
250 r.extend(sibling.traverse(include_self=1, descend=descend,
251 siblings=0, ascend=0,
252 condition=condition))
253 if not ascend:
254 break
255 else:
256 node = node.parent
257 return r
259 def next_node(self, condition=None,
260 include_self=0, descend=1, siblings=0, ascend=0):
262 Return the first node in the iterable returned by traverse(),
263 or None if the iterable is empty.
265 Parameter list is the same as of traverse. Note that
266 include_self defaults to 0, though.
268 iterable = self.traverse(condition=condition,
269 include_self=include_self, descend=descend,
270 siblings=siblings, ascend=ascend)
271 try:
272 return iterable[0]
273 except IndexError:
274 return None
276 class Text(Node, UserString):
279 Instances are terminal nodes (leaves) containing text only; no child
280 nodes or attributes. Initialize by passing a string to the constructor.
281 Access the text itself with the `astext` method.
284 tagname = '#text'
286 children = ()
287 """Text nodes have no children, and cannot have children."""
289 def __init__(self, data, rawsource=''):
290 UserString.__init__(self, data)
292 self.rawsource = rawsource
293 """The raw text from which this element was constructed."""
295 def __repr__(self):
296 data = repr(self.data)
297 if len(data) > 70:
298 data = repr(self.data[:64] + ' ...')
299 return '<%s: %s>' % (self.tagname, data)
301 def __len__(self):
302 return len(self.data)
304 def shortrepr(self):
305 data = repr(self.data)
306 if len(data) > 20:
307 data = repr(self.data[:16] + ' ...')
308 return '<%s: %s>' % (self.tagname, data)
310 def _dom_node(self, domroot):
311 return domroot.createTextNode(self.data)
313 def astext(self):
314 return self.data
316 def __unicode__(self):
317 return self.data
319 def copy(self):
320 return self.__class__(self.data)
322 def deepcopy(self):
323 return self.copy()
325 def pformat(self, indent=' ', level=0):
326 result = []
327 indent = indent * level
328 for line in self.data.splitlines():
329 result.append(indent + line + '\n')
330 return ''.join(result)
333 class Element(Node):
336 `Element` is the superclass to all specific elements.
338 Elements contain attributes and child nodes. Elements emulate
339 dictionaries for attributes, indexing by attribute name (a string). To
340 set the attribute 'att' to 'value', do::
342 element['att'] = 'value'
344 There are two special attributes: 'ids' and 'names'. Both are
345 lists of unique identifiers, and names serve as human interfaces
346 to IDs. Names are case- and whitespace-normalized (see the
347 fully_normalize_name() function), and IDs conform to the regular
348 expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
350 Elements also emulate lists for child nodes (element nodes and/or text
351 nodes), indexing by integer. To get the first child node, use::
353 element[0]
355 Elements may be constructed using the ``+=`` operator. To add one new
356 child node to element, do::
358 element += node
360 This is equivalent to ``element.append(node)``.
362 To add a list of multiple child nodes at once, use the same ``+=``
363 operator::
365 element += [node1, node2]
367 This is equivalent to ``element.extend([node1, node2])``.
370 list_attributes = ('ids', 'classes', 'names', 'dupnames', 'backrefs')
371 """List attributes, automatically initialized to empty lists for
372 all nodes."""
374 tagname = None
375 """The element generic identifier. If None, it is set as an instance
376 attribute to the name of the class."""
378 child_text_separator = '\n\n'
379 """Separator for child nodes, used by `astext()` method."""
381 def __init__(self, rawsource='', *children, **attributes):
382 self.rawsource = rawsource
383 """The raw text from which this element was constructed."""
385 self.children = []
386 """List of child nodes (elements and/or `Text`)."""
388 self.extend(children) # maintain parent info
390 self.attributes = {}
391 """Dictionary of attribute {name: value}."""
393 # Initialize list attributes.
394 for att in self.list_attributes:
395 self.attributes[att] = []
397 for att, value in attributes.items():
398 att = att.lower()
399 if att in self.list_attributes:
400 # mutable list; make a copy for this node
401 self.attributes[att] = value[:]
402 else:
403 self.attributes[att] = value
405 if self.tagname is None:
406 self.tagname = self.__class__.__name__
408 def _dom_node(self, domroot):
409 element = domroot.createElement(self.tagname)
410 for attribute, value in self.attlist():
411 if isinstance(value, ListType):
412 value = ' '.join([serial_escape('%s' % v) for v in value])
413 element.setAttribute(attribute, '%s' % value)
414 for child in self.children:
415 element.appendChild(child._dom_node(domroot))
416 return element
418 def __repr__(self):
419 data = ''
420 for c in self.children:
421 data += c.shortrepr()
422 if len(data) > 60:
423 data = data[:56] + ' ...'
424 break
425 if self['names']:
426 return '<%s "%s": %s>' % (self.__class__.__name__,
427 '; '.join(self['names']), data)
428 else:
429 return '<%s: %s>' % (self.__class__.__name__, data)
431 def shortrepr(self):
432 if self['names']:
433 return '<%s "%s"...>' % (self.__class__.__name__,
434 '; '.join(self['names']))
435 else:
436 return '<%s...>' % self.tagname
438 def __unicode__(self):
439 if self.children:
440 return u'%s%s%s' % (self.starttag(),
441 ''.join([unicode(c) for c in self.children]),
442 self.endtag())
443 else:
444 return self.emptytag()
446 def starttag(self):
447 parts = [self.tagname]
448 for name, value in self.attlist():
449 if value is None: # boolean attribute
450 parts.append(name)
451 elif isinstance(value, ListType):
452 values = [serial_escape('%s' % v) for v in value]
453 parts.append('%s="%s"' % (name, ' '.join(values)))
454 else:
455 parts.append('%s="%s"' % (name, value))
456 return '<%s>' % ' '.join(parts)
458 def endtag(self):
459 return '</%s>' % self.tagname
461 def emptytag(self):
462 return u'<%s/>' % ' '.join([self.tagname] +
463 ['%s="%s"' % (n, v)
464 for n, v in self.attlist()])
466 def __len__(self):
467 return len(self.children)
469 def __getitem__(self, key):
470 if isinstance(key, UnicodeType) or isinstance(key, StringType):
471 return self.attributes[key]
472 elif isinstance(key, IntType):
473 return self.children[key]
474 elif isinstance(key, SliceType):
475 assert key.step in (None, 1), 'cannot handle slice with stride'
476 return self.children[key.start:key.stop]
477 else:
478 raise TypeError, ('element index must be an integer, a slice, or '
479 'an attribute name string')
481 def __setitem__(self, key, item):
482 if isinstance(key, UnicodeType) or isinstance(key, StringType):
483 self.attributes[str(key)] = item
484 elif isinstance(key, IntType):
485 self.setup_child(item)
486 self.children[key] = item
487 elif isinstance(key, SliceType):
488 assert key.step in (None, 1), 'cannot handle slice with stride'
489 for node in item:
490 self.setup_child(node)
491 self.children[key.start:key.stop] = item
492 else:
493 raise TypeError, ('element index must be an integer, a slice, or '
494 'an attribute name string')
496 def __delitem__(self, key):
497 if isinstance(key, UnicodeType) or isinstance(key, StringType):
498 del self.attributes[key]
499 elif isinstance(key, IntType):
500 del self.children[key]
501 elif isinstance(key, SliceType):
502 assert key.step in (None, 1), 'cannot handle slice with stride'
503 del self.children[key.start:key.stop]
504 else:
505 raise TypeError, ('element index must be an integer, a simple '
506 'slice, or an attribute name string')
508 def __add__(self, other):
509 return self.children + other
511 def __radd__(self, other):
512 return other + self.children
514 def __iadd__(self, other):
515 """Append a node or a list of nodes to `self.children`."""
516 if isinstance(other, Node):
517 self.append(other)
518 elif other is not None:
519 self.extend(other)
520 return self
522 def astext(self):
523 return self.child_text_separator.join(
524 [child.astext() for child in self.children])
526 def non_default_attributes(self):
527 atts = {}
528 for key, value in self.attributes.items():
529 if self.is_not_default(key):
530 atts[key] = value
531 return atts
533 def attlist(self):
534 attlist = self.non_default_attributes().items()
535 attlist.sort()
536 return attlist
538 def get(self, key, failobj=None):
539 return self.attributes.get(key, failobj)
541 def hasattr(self, attr):
542 return self.attributes.has_key(attr)
544 def delattr(self, attr):
545 if self.attributes.has_key(attr):
546 del self.attributes[attr]
548 def setdefault(self, key, failobj=None):
549 return self.attributes.setdefault(key, failobj)
551 has_key = hasattr
553 def append(self, item):
554 self.setup_child(item)
555 self.children.append(item)
557 def extend(self, item):
558 for node in item:
559 self.append(node)
561 def insert(self, index, item):
562 if isinstance(item, Node):
563 self.setup_child(item)
564 self.children.insert(index, item)
565 elif item is not None:
566 self[index:index] = item
568 def pop(self, i=-1):
569 return self.children.pop(i)
571 def remove(self, item):
572 self.children.remove(item)
574 def index(self, item):
575 return self.children.index(item)
577 def is_not_default(self, key):
578 if self[key] == [] and key in self.list_attributes:
579 return 0
580 else:
581 return 1
583 def update_basic_atts(self, dict):
585 Update basic attributes ('ids', 'names', 'classes',
586 'dupnames', but not 'source') from node or dictionary `dict`.
588 if isinstance(dict, Node):
589 dict = dict.attributes
590 for att in ('ids', 'classes', 'names', 'dupnames'):
591 for value in dict.get(att, []):
592 if not value in self[att]:
593 self[att].append(value)
595 def clear(self):
596 self.children = []
598 def replace(self, old, new):
599 """Replace one child `Node` with another child or children."""
600 index = self.index(old)
601 if isinstance(new, Node):
602 self.setup_child(new)
603 self[index] = new
604 elif new is not None:
605 self[index:index+1] = new
607 def replace_self(self, new):
609 Replace `self` node with `new`, where `new` is a node or a
610 list of nodes.
612 update = new
613 if not isinstance(new, Node):
614 # `new` is a list; update first child.
615 try:
616 update = new[0]
617 except IndexError:
618 update = None
619 if isinstance(update, Element):
620 update.update_basic_atts(self)
621 else:
622 # `update` is a Text node or `new` is an empty list.
623 # Assert that we aren't losing any attributes.
624 for att in ('ids', 'names', 'classes', 'dupnames'):
625 assert not self[att], \
626 'Losing "%s" attribute: %s' % (att, self[att])
627 self.parent.replace(self, new)
629 def first_child_matching_class(self, childclass, start=0, end=sys.maxint):
631 Return the index of the first child whose class exactly matches.
633 Parameters:
635 - `childclass`: A `Node` subclass to search for, or a tuple of `Node`
636 classes. If a tuple, any of the classes may match.
637 - `start`: Initial index to check.
638 - `end`: Initial index to *not* check.
640 if not isinstance(childclass, TupleType):
641 childclass = (childclass,)
642 for index in range(start, min(len(self), end)):
643 for c in childclass:
644 if isinstance(self[index], c):
645 return index
646 return None
648 def first_child_not_matching_class(self, childclass, start=0,
649 end=sys.maxint):
651 Return the index of the first child whose class does *not* match.
653 Parameters:
655 - `childclass`: A `Node` subclass to skip, or a tuple of `Node`
656 classes. If a tuple, none of the classes may match.
657 - `start`: Initial index to check.
658 - `end`: Initial index to *not* check.
660 if not isinstance(childclass, TupleType):
661 childclass = (childclass,)
662 for index in range(start, min(len(self), end)):
663 for c in childclass:
664 if isinstance(self.children[index], c):
665 break
666 else:
667 return index
668 return None
670 def pformat(self, indent=' ', level=0):
671 return ''.join(['%s%s\n' % (indent * level, self.starttag())] +
672 [child.pformat(indent, level+1)
673 for child in self.children])
675 def copy(self):
676 return self.__class__(**self.attributes)
678 def deepcopy(self):
679 copy = self.copy()
680 copy.extend([child.deepcopy() for child in self.children])
681 return copy
683 def set_class(self, name):
684 """Add a new class to the "classes" attribute."""
685 warnings.warn('docutils.nodes.Element.set_class deprecated; '
686 "append to Element['classes'] list attribute directly",
687 DeprecationWarning, stacklevel=2)
688 assert ' ' not in name
689 self['classes'].append(name.lower())
691 def note_referenced_by(self, name=None, id=None):
692 """Note that this Element has been referenced by its name
693 `name` or id `id`."""
694 self.referenced = 1
695 # Element.expect_referenced_by_* dictionaries map names or ids
696 # to nodes whose ``referenced`` attribute is set to true as
697 # soon as this node is referenced by the given name or id.
698 # Needed for target propagation.
699 by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
700 by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
701 if by_name:
702 assert name is not None
703 by_name.referenced = 1
704 if by_id:
705 assert id is not None
706 by_id.referenced = 1
709 class TextElement(Element):
712 An element which directly contains text.
714 Its children are all `Text` or `Inline` subclass nodes. You can
715 check whether an element's context is inline simply by checking whether
716 its immediate parent is a `TextElement` instance (including subclasses).
717 This is handy for nodes like `image` that can appear both inline and as
718 standalone body elements.
720 If passing children to `__init__()`, make sure to set `text` to
721 ``''`` or some other suitable value.
724 child_text_separator = ''
725 """Separator for child nodes, used by `astext()` method."""
727 def __init__(self, rawsource='', text='', *children, **attributes):
728 if text != '':
729 textnode = Text(text)
730 Element.__init__(self, rawsource, textnode, *children,
731 **attributes)
732 else:
733 Element.__init__(self, rawsource, *children, **attributes)
736 class FixedTextElement(TextElement):
738 """An element which directly contains preformatted text."""
740 def __init__(self, rawsource='', text='', *children, **attributes):
741 TextElement.__init__(self, rawsource, text, *children, **attributes)
742 self.attributes['xml:space'] = 'preserve'
745 # ========
746 # Mixins
747 # ========
749 class Resolvable:
751 resolved = 0
754 class BackLinkable:
756 def add_backref(self, refid):
757 self['backrefs'].append(refid)
760 # ====================
761 # Element Categories
762 # ====================
764 class Root: pass
766 class Titular: pass
768 class PreBibliographic:
769 """Category of Node which may occur before Bibliographic Nodes."""
771 class Bibliographic: pass
773 class Decorative(PreBibliographic): pass
775 class Structural: pass
777 class Body: pass
779 class General(Body): pass
781 class Sequential(Body):
782 """List-like elements."""
784 class Admonition(Body): pass
786 class Special(Body):
787 """Special internal body elements."""
789 class Invisible(PreBibliographic):
790 """Internal elements that don't appear in output."""
792 class Part: pass
794 class Inline: pass
796 class Referential(Resolvable): pass
799 class Targetable(Resolvable):
801 referenced = 0
803 indirect_reference_name = None
804 """Holds the whitespace_normalized_name (contains mixed case) of a target.
805 Required for MoinMoin/reST compatibility."""
808 class Labeled:
809 """Contains a `label` as its first element."""
812 # ==============
813 # Root Element
814 # ==============
816 class document(Root, Structural, Element):
819 The document root element.
821 Do not instantiate this class directly; use
822 `docutils.utils.new_document()` instead.
825 def __init__(self, settings, reporter, *args, **kwargs):
826 Element.__init__(self, *args, **kwargs)
828 self.current_source = None
829 """Path to or description of the input source being processed."""
831 self.current_line = None
832 """Line number (1-based) of `current_source`."""
834 self.settings = settings
835 """Runtime settings data record."""
837 self.reporter = reporter
838 """System message generator."""
840 self.indirect_targets = []
841 """List of indirect target nodes."""
843 self.substitution_defs = {}
844 """Mapping of substitution names to substitution_definition nodes."""
846 self.substitution_names = {}
847 """Mapping of case-normalized substitution names to case-sensitive
848 names."""
850 self.refnames = {}
851 """Mapping of names to lists of referencing nodes."""
853 self.refids = {}
854 """Mapping of ids to lists of referencing nodes."""
856 self.nameids = {}
857 """Mapping of names to unique id's."""
859 self.nametypes = {}
860 """Mapping of names to hyperlink type (boolean: True => explicit,
861 False => implicit."""
863 self.ids = {}
864 """Mapping of ids to nodes."""
866 self.footnote_refs = {}
867 """Mapping of footnote labels to lists of footnote_reference nodes."""
869 self.citation_refs = {}
870 """Mapping of citation labels to lists of citation_reference nodes."""
872 self.autofootnotes = []
873 """List of auto-numbered footnote nodes."""
875 self.autofootnote_refs = []
876 """List of auto-numbered footnote_reference nodes."""
878 self.symbol_footnotes = []
879 """List of symbol footnote nodes."""
881 self.symbol_footnote_refs = []
882 """List of symbol footnote_reference nodes."""
884 self.footnotes = []
885 """List of manually-numbered footnote nodes."""
887 self.citations = []
888 """List of citation nodes."""
890 self.autofootnote_start = 1
891 """Initial auto-numbered footnote number."""
893 self.symbol_footnote_start = 0
894 """Initial symbol footnote symbol index."""
896 self.id_start = 1
897 """Initial ID number."""
899 self.parse_messages = []
900 """System messages generated while parsing."""
902 self.transform_messages = []
903 """System messages generated while applying transforms."""
905 import docutils.transforms
906 self.transformer = docutils.transforms.Transformer(self)
907 """Storage for transforms to be applied to this document."""
909 self.decoration = None
910 """Document's `decoration` node."""
912 self.document = self
914 def __getstate__(self):
916 Return dict with unpicklable references removed.
918 state = self.__dict__.copy()
919 state['reporter'] = None
920 state['transformer'] = None
921 return state
923 def asdom(self, dom=None):
924 """Return a DOM representation of this document."""
925 if dom is None:
926 import xml.dom.minidom as dom
927 domroot = dom.Document()
928 domroot.appendChild(self._dom_node(domroot))
929 return domroot
931 def set_id(self, node, msgnode=None):
932 for id in node['ids']:
933 if self.ids.has_key(id) and self.ids[id] is not node:
934 msg = self.reporter.severe('Duplicate ID: "%s".' % id)
935 if msgnode != None:
936 msgnode += msg
937 if not node['ids']:
938 for name in node['names']:
939 id = self.settings.id_prefix + make_id(name)
940 if id and not self.ids.has_key(id):
941 break
942 else:
943 id = ''
944 while not id or self.ids.has_key(id):
945 id = (self.settings.id_prefix +
946 self.settings.auto_id_prefix + str(self.id_start))
947 self.id_start += 1
948 node['ids'].append(id)
949 self.ids[id] = node
950 return id
952 def set_name_id_map(self, node, id, msgnode=None, explicit=None):
954 `self.nameids` maps names to IDs, while `self.nametypes` maps names to
955 booleans representing hyperlink type (True==explicit,
956 False==implicit). This method updates the mappings.
958 The following state transition table shows how `self.nameids` ("ids")
959 and `self.nametypes` ("types") change with new input (a call to this
960 method), and what actions are performed ("implicit"-type system
961 messages are INFO/1, and "explicit"-type system messages are ERROR/3):
963 ==== ===== ======== ======== ======= ==== ===== =====
964 Old State Input Action New State Notes
965 ----------- -------- ----------------- ----------- -----
966 ids types new type sys.msg. dupname ids types
967 ==== ===== ======== ======== ======= ==== ===== =====
968 - - explicit - - new True
969 - - implicit - - new False
970 None False explicit - - new True
971 old False explicit implicit old new True
972 None True explicit explicit new None True
973 old True explicit explicit new,old None True [#]_
974 None False implicit implicit new None False
975 old False implicit implicit new,old None False
976 None True implicit implicit new None True
977 old True implicit implicit new old True
978 ==== ===== ======== ======== ======= ==== ===== =====
980 .. [#] Do not clear the name-to-id map or invalidate the old target if
981 both old and new targets are external and refer to identical URIs.
982 The new target is invalidated regardless.
984 for name in node['names']:
985 if self.nameids.has_key(name):
986 self.set_duplicate_name_id(node, id, name, msgnode, explicit)
987 else:
988 self.nameids[name] = id
989 self.nametypes[name] = explicit
991 def set_duplicate_name_id(self, node, id, name, msgnode, explicit):
992 old_id = self.nameids[name]
993 old_explicit = self.nametypes[name]
994 self.nametypes[name] = old_explicit or explicit
995 if explicit:
996 if old_explicit:
997 level = 2
998 if old_id is not None:
999 old_node = self.ids[old_id]
1000 if node.has_key('refuri'):
1001 refuri = node['refuri']
1002 if old_node['names'] \
1003 and old_node.has_key('refuri') \
1004 and old_node['refuri'] == refuri:
1005 level = 1 # just inform if refuri's identical
1006 if level > 1:
1007 dupname(old_node, name)
1008 self.nameids[name] = None
1009 msg = self.reporter.system_message(
1010 level, 'Duplicate explicit target name: "%s".' % name,
1011 backrefs=[id], base_node=node)
1012 if msgnode != None:
1013 msgnode += msg
1014 dupname(node, name)
1015 else:
1016 self.nameids[name] = id
1017 if old_id is not None:
1018 old_node = self.ids[old_id]
1019 dupname(old_node, name)
1020 else:
1021 if old_id is not None and not old_explicit:
1022 self.nameids[name] = None
1023 old_node = self.ids[old_id]
1024 dupname(old_node, name)
1025 dupname(node, name)
1026 if not explicit or (not old_explicit and old_id is not None):
1027 msg = self.reporter.info(
1028 'Duplicate implicit target name: "%s".' % name,
1029 backrefs=[id], base_node=node)
1030 if msgnode != None:
1031 msgnode += msg
1033 def has_name(self, name):
1034 return self.nameids.has_key(name)
1036 # "note" here is an imperative verb: "take note of".
1037 def note_implicit_target(self, target, msgnode=None):
1038 id = self.set_id(target, msgnode)
1039 self.set_name_id_map(target, id, msgnode, explicit=None)
1041 def note_explicit_target(self, target, msgnode=None):
1042 id = self.set_id(target, msgnode)
1043 self.set_name_id_map(target, id, msgnode, explicit=1)
1045 def note_refname(self, node):
1046 self.refnames.setdefault(node['refname'], []).append(node)
1048 def note_refid(self, node):
1049 self.refids.setdefault(node['refid'], []).append(node)
1051 def note_indirect_target(self, target):
1052 self.indirect_targets.append(target)
1053 if target['names']:
1054 self.note_refname(target)
1056 def note_anonymous_target(self, target):
1057 self.set_id(target)
1059 def note_autofootnote(self, footnote):
1060 self.set_id(footnote)
1061 self.autofootnotes.append(footnote)
1063 def note_autofootnote_ref(self, ref):
1064 self.set_id(ref)
1065 self.autofootnote_refs.append(ref)
1067 def note_symbol_footnote(self, footnote):
1068 self.set_id(footnote)
1069 self.symbol_footnotes.append(footnote)
1071 def note_symbol_footnote_ref(self, ref):
1072 self.set_id(ref)
1073 self.symbol_footnote_refs.append(ref)
1075 def note_footnote(self, footnote):
1076 self.set_id(footnote)
1077 self.footnotes.append(footnote)
1079 def note_footnote_ref(self, ref):
1080 self.set_id(ref)
1081 self.footnote_refs.setdefault(ref['refname'], []).append(ref)
1082 self.note_refname(ref)
1084 def note_citation(self, citation):
1085 self.citations.append(citation)
1087 def note_citation_ref(self, ref):
1088 self.set_id(ref)
1089 self.citation_refs.setdefault(ref['refname'], []).append(ref)
1090 self.note_refname(ref)
1092 def note_substitution_def(self, subdef, def_name, msgnode=None):
1093 name = whitespace_normalize_name(def_name)
1094 if self.substitution_defs.has_key(name):
1095 msg = self.reporter.error(
1096 'Duplicate substitution definition name: "%s".' % name,
1097 base_node=subdef)
1098 if msgnode != None:
1099 msgnode += msg
1100 oldnode = self.substitution_defs[name]
1101 dupname(oldnode, name)
1102 # keep only the last definition:
1103 self.substitution_defs[name] = subdef
1104 # case-insensitive mapping:
1105 self.substitution_names[fully_normalize_name(name)] = name
1107 def note_substitution_ref(self, subref, refname):
1108 subref['refname'] = whitespace_normalize_name(refname)
1110 def note_pending(self, pending, priority=None):
1111 self.transformer.add_pending(pending, priority)
1113 def note_parse_message(self, message):
1114 self.parse_messages.append(message)
1116 def note_transform_message(self, message):
1117 self.transform_messages.append(message)
1119 def note_source(self, source, offset):
1120 self.current_source = source
1121 if offset is None:
1122 self.current_line = offset
1123 else:
1124 self.current_line = offset + 1
1126 def copy(self):
1127 return self.__class__(self.settings, self.reporter,
1128 **self.attributes)
1130 def get_decoration(self):
1131 if not self.decoration:
1132 self.decoration = decoration()
1133 index = self.first_child_not_matching_class(Titular)
1134 if index is None:
1135 self.append(self.decoration)
1136 else:
1137 self.insert(index, self.decoration)
1138 return self.decoration
1141 # ================
1142 # Title Elements
1143 # ================
1145 class title(Titular, PreBibliographic, TextElement): pass
1146 class subtitle(Titular, PreBibliographic, TextElement): pass
1147 class rubric(Titular, TextElement): pass
1150 # ========================
1151 # Bibliographic Elements
1152 # ========================
1154 class docinfo(Bibliographic, Element): pass
1155 class author(Bibliographic, TextElement): pass
1156 class authors(Bibliographic, Element): pass
1157 class organization(Bibliographic, TextElement): pass
1158 class address(Bibliographic, FixedTextElement): pass
1159 class contact(Bibliographic, TextElement): pass
1160 class version(Bibliographic, TextElement): pass
1161 class revision(Bibliographic, TextElement): pass
1162 class status(Bibliographic, TextElement): pass
1163 class date(Bibliographic, TextElement): pass
1164 class copyright(Bibliographic, TextElement): pass
1167 # =====================
1168 # Decorative Elements
1169 # =====================
1171 class decoration(Decorative, Element):
1173 def get_header(self):
1174 if not len(self.children) or not isinstance(self.children[0], header):
1175 self.insert(0, header())
1176 return self.children[0]
1178 def get_footer(self):
1179 if not len(self.children) or not isinstance(self.children[-1], footer):
1180 self.append(footer())
1181 return self.children[-1]
1184 class header(Decorative, Element): pass
1185 class footer(Decorative, Element): pass
1188 # =====================
1189 # Structural Elements
1190 # =====================
1192 class section(Structural, Element): pass
1195 class topic(Structural, Element):
1198 Topics are terminal, "leaf" mini-sections, like block quotes with titles,
1199 or textual figures. A topic is just like a section, except that it has no
1200 subsections, and it doesn't have to conform to section placement rules.
1202 Topics are allowed wherever body elements (list, table, etc.) are allowed,
1203 but only at the top level of a section or document. Topics cannot nest
1204 inside topics, sidebars, or body elements; you can't have a topic inside a
1205 table, list, block quote, etc.
1209 class sidebar(Structural, Element):
1212 Sidebars are like miniature, parallel documents that occur inside other
1213 documents, providing related or reference material. A sidebar is
1214 typically offset by a border and "floats" to the side of the page; the
1215 document's main text may flow around it. Sidebars can also be likened to
1216 super-footnotes; their content is outside of the flow of the document's
1217 main text.
1219 Sidebars are allowed wherever body elements (list, table, etc.) are
1220 allowed, but only at the top level of a section or document. Sidebars
1221 cannot nest inside sidebars, topics, or body elements; you can't have a
1222 sidebar inside a table, list, block quote, etc.
1226 class transition(Structural, Element): pass
1229 # ===============
1230 # Body Elements
1231 # ===============
1233 class paragraph(General, TextElement): pass
1234 class compound(General, Element): pass
1235 class container(General, Element): pass
1236 class bullet_list(Sequential, Element): pass
1237 class enumerated_list(Sequential, Element): pass
1238 class list_item(Part, Element): pass
1239 class definition_list(Sequential, Element): pass
1240 class definition_list_item(Part, Element): pass
1241 class term(Part, TextElement): pass
1242 class classifier(Part, TextElement): pass
1243 class definition(Part, Element): pass
1244 class field_list(Sequential, Element): pass
1245 class field(Part, Element): pass
1246 class field_name(Part, TextElement): pass
1247 class field_body(Part, Element): pass
1250 class option(Part, Element):
1252 child_text_separator = ''
1255 class option_argument(Part, TextElement):
1257 def astext(self):
1258 return self.get('delimiter', ' ') + TextElement.astext(self)
1261 class option_group(Part, Element):
1263 child_text_separator = ', '
1266 class option_list(Sequential, Element): pass
1269 class option_list_item(Part, Element):
1271 child_text_separator = ' '
1274 class option_string(Part, TextElement): pass
1275 class description(Part, Element): pass
1276 class literal_block(General, FixedTextElement): pass
1277 class doctest_block(General, FixedTextElement): pass
1278 class line_block(General, Element): pass
1281 class line(Part, TextElement):
1283 indent = None
1286 class block_quote(General, Element): pass
1287 class attribution(Part, TextElement): pass
1288 class attention(Admonition, Element): pass
1289 class caution(Admonition, Element): pass
1290 class danger(Admonition, Element): pass
1291 class error(Admonition, Element): pass
1292 class important(Admonition, Element): pass
1293 class note(Admonition, Element): pass
1294 class tip(Admonition, Element): pass
1295 class hint(Admonition, Element): pass
1296 class warning(Admonition, Element): pass
1297 class admonition(Admonition, Element): pass
1298 class comment(Special, Invisible, FixedTextElement): pass
1299 class substitution_definition(Special, Invisible, TextElement): pass
1300 class target(Special, Invisible, Inline, TextElement, Targetable): pass
1301 class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
1302 class citation(General, BackLinkable, Element, Labeled, Targetable): pass
1303 class label(Part, TextElement): pass
1304 class figure(General, Element): pass
1305 class caption(Part, TextElement): pass
1306 class legend(Part, Element): pass
1307 class table(General, Element): pass
1308 class tgroup(Part, Element): pass
1309 class colspec(Part, Element): pass
1310 class thead(Part, Element): pass
1311 class tbody(Part, Element): pass
1312 class row(Part, Element): pass
1313 class entry(Part, Element): pass
1316 class system_message(Special, BackLinkable, PreBibliographic, Element):
1319 System message element.
1321 Do not instantiate this class directly; use
1322 ``document.reporter.info/warning/error/severe()`` instead.
1325 def __init__(self, message=None, *children, **attributes):
1326 if message:
1327 p = paragraph('', message)
1328 children = (p,) + children
1329 try:
1330 Element.__init__(self, '', *children, **attributes)
1331 except:
1332 print 'system_message: children=%r' % (children,)
1333 raise
1335 def astext(self):
1336 line = self.get('line', '')
1337 return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
1338 self['level'], Element.astext(self))
1341 class pending(Special, Invisible, Element):
1344 The "pending" element is used to encapsulate a pending operation: the
1345 operation (transform), the point at which to apply it, and any data it
1346 requires. Only the pending operation's location within the document is
1347 stored in the public document tree (by the "pending" object itself); the
1348 operation and its data are stored in the "pending" object's internal
1349 instance attributes.
1351 For example, say you want a table of contents in your reStructuredText
1352 document. The easiest way to specify where to put it is from within the
1353 document, with a directive::
1355 .. contents::
1357 But the "contents" directive can't do its work until the entire document
1358 has been parsed and possibly transformed to some extent. So the directive
1359 code leaves a placeholder behind that will trigger the second phase of its
1360 processing, something like this::
1362 <pending ...public attributes...> + internal attributes
1364 Use `document.note_pending()` so that the
1365 `docutils.transforms.Transformer` stage of processing can run all pending
1366 transforms.
1369 def __init__(self, transform, details=None,
1370 rawsource='', *children, **attributes):
1371 Element.__init__(self, rawsource, *children, **attributes)
1373 self.transform = transform
1374 """The `docutils.transforms.Transform` class implementing the pending
1375 operation."""
1377 self.details = details or {}
1378 """Detail data (dictionary) required by the pending operation."""
1380 def pformat(self, indent=' ', level=0):
1381 internals = [
1382 '.. internal attributes:',
1383 ' .transform: %s.%s' % (self.transform.__module__,
1384 self.transform.__name__),
1385 ' .details:']
1386 details = self.details.items()
1387 details.sort()
1388 for key, value in details:
1389 if isinstance(value, Node):
1390 internals.append('%7s%s:' % ('', key))
1391 internals.extend(['%9s%s' % ('', line)
1392 for line in value.pformat().splitlines()])
1393 elif value and isinstance(value, ListType) \
1394 and isinstance(value[0], Node):
1395 internals.append('%7s%s:' % ('', key))
1396 for v in value:
1397 internals.extend(['%9s%s' % ('', line)
1398 for line in v.pformat().splitlines()])
1399 else:
1400 internals.append('%7s%s: %r' % ('', key, value))
1401 return (Element.pformat(self, indent, level)
1402 + ''.join([(' %s%s\n' % (indent * level, line))
1403 for line in internals]))
1405 def copy(self):
1406 return self.__class__(self.transform, self.details, self.rawsource,
1407 **self.attributes)
1410 class raw(Special, Inline, PreBibliographic, FixedTextElement):
1413 Raw data that is to be passed untouched to the Writer.
1416 pass
1419 # =================
1420 # Inline Elements
1421 # =================
1423 class emphasis(Inline, TextElement): pass
1424 class strong(Inline, TextElement): pass
1425 class literal(Inline, TextElement): pass
1426 class reference(General, Inline, Referential, TextElement): pass
1427 class footnote_reference(Inline, Referential, TextElement): pass
1428 class citation_reference(Inline, Referential, TextElement): pass
1429 class substitution_reference(Inline, TextElement): pass
1430 class title_reference(Inline, TextElement): pass
1431 class abbreviation(Inline, TextElement): pass
1432 class acronym(Inline, TextElement): pass
1433 class superscript(Inline, TextElement): pass
1434 class subscript(Inline, TextElement): pass
1437 class image(General, Inline, Element):
1439 def astext(self):
1440 return self.get('alt', '')
1443 class inline(Inline, TextElement): pass
1444 class problematic(Inline, TextElement): pass
1445 class generated(Inline, TextElement): pass
1448 # ========================================
1449 # Auxiliary Classes, Functions, and Data
1450 # ========================================
1452 node_class_names = """
1453 Text
1454 abbreviation acronym address admonition attention attribution author
1455 authors
1456 block_quote bullet_list
1457 caption caution citation citation_reference classifier colspec comment
1458 compound contact container copyright
1459 danger date decoration definition definition_list definition_list_item
1460 description docinfo doctest_block document
1461 emphasis entry enumerated_list error
1462 field field_body field_list field_name figure footer
1463 footnote footnote_reference
1464 generated
1465 header hint
1466 image important inline
1467 label legend line line_block list_item literal literal_block
1468 note
1469 option option_argument option_group option_list option_list_item
1470 option_string organization
1471 paragraph pending problematic
1472 raw reference revision row rubric
1473 section sidebar status strong subscript substitution_definition
1474 substitution_reference subtitle superscript system_message
1475 table target tbody term tgroup thead tip title title_reference topic
1476 transition
1477 version
1478 warning""".split()
1479 """A list of names of all concrete Node subclasses."""
1482 class NodeVisitor:
1485 "Visitor" pattern [GoF95]_ abstract superclass implementation for
1486 document tree traversals.
1488 Each node class has corresponding methods, doing nothing by
1489 default; override individual methods for specific and useful
1490 behaviour. The `dispatch_visit()` method is called by
1491 `Node.walk()` upon entering a node. `Node.walkabout()` also calls
1492 the `dispatch_departure()` method before exiting a node.
1494 The dispatch methods call "``visit_`` + node class name" or
1495 "``depart_`` + node class name", resp.
1497 This is a base class for visitors whose ``visit_...`` & ``depart_...``
1498 methods should be implemented for *all* node types encountered (such as
1499 for `docutils.writers.Writer` subclasses). Unimplemented methods will
1500 raise exceptions.
1502 For sparse traversals, where only certain node types are of interest,
1503 subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform
1504 processing is desired, subclass `GenericNodeVisitor`.
1506 .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
1507 Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
1508 1995.
1511 optional = ()
1513 Tuple containing node class names (as strings).
1515 No exception will be raised if writers do not implement visit
1516 or departure functions for these node classes.
1518 Used to ensure transitional compatibility with existing 3rd-party writers.
1521 def __init__(self, document):
1522 self.document = document
1524 def dispatch_visit(self, node):
1526 Call self."``visit_`` + node class name" with `node` as
1527 parameter. If the ``visit_...`` method does not exist, call
1528 self.unknown_visit.
1530 node_name = node.__class__.__name__
1531 method = getattr(self, 'visit_' + node_name, self.unknown_visit)
1532 self.document.reporter.debug(
1533 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
1534 % (method.__name__, node_name))
1535 return method(node)
1537 def dispatch_departure(self, node):
1539 Call self."``depart_`` + node class name" with `node` as
1540 parameter. If the ``depart_...`` method does not exist, call
1541 self.unknown_departure.
1543 node_name = node.__class__.__name__
1544 method = getattr(self, 'depart_' + node_name, self.unknown_departure)
1545 self.document.reporter.debug(
1546 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
1547 % (method.__name__, node_name))
1548 return method(node)
1550 def unknown_visit(self, node):
1552 Called when entering unknown `Node` types.
1554 Raise an exception unless overridden.
1556 if (node.document.settings.strict_visitor
1557 or node.__class__.__name__ not in self.optional):
1558 raise NotImplementedError(
1559 '%s visiting unknown node type: %s'
1560 % (self.__class__, node.__class__.__name__))
1562 def unknown_departure(self, node):
1564 Called before exiting unknown `Node` types.
1566 Raise exception unless overridden.
1568 if (node.document.settings.strict_visitor
1569 or node.__class__.__name__ not in self.optional):
1570 raise NotImplementedError(
1571 '%s departing unknown node type: %s'
1572 % (self.__class__, node.__class__.__name__))
1575 class SparseNodeVisitor(NodeVisitor):
1578 Base class for sparse traversals, where only certain node types are of
1579 interest. When ``visit_...`` & ``depart_...`` methods should be
1580 implemented for *all* node types (such as for `docutils.writers.Writer`
1581 subclasses), subclass `NodeVisitor` instead.
1585 class GenericNodeVisitor(NodeVisitor):
1588 Generic "Visitor" abstract superclass, for simple traversals.
1590 Unless overridden, each ``visit_...`` method calls `default_visit()`, and
1591 each ``depart_...`` method (when using `Node.walkabout()`) calls
1592 `default_departure()`. `default_visit()` (and `default_departure()`) must
1593 be overridden in subclasses.
1595 Define fully generic visitors by overriding `default_visit()` (and
1596 `default_departure()`) only. Define semi-generic visitors by overriding
1597 individual ``visit_...()`` (and ``depart_...()``) methods also.
1599 `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should
1600 be overridden for default behavior.
1603 def default_visit(self, node):
1604 """Override for generic, uniform traversals."""
1605 raise NotImplementedError
1607 def default_departure(self, node):
1608 """Override for generic, uniform traversals."""
1609 raise NotImplementedError
1611 def _call_default_visit(self, node):
1612 self.default_visit(node)
1614 def _call_default_departure(self, node):
1615 self.default_departure(node)
1617 def _nop(self, node):
1618 pass
1620 def _add_node_class_names(names):
1621 """Save typing with dynamic assignments:"""
1622 for _name in names:
1623 setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
1624 setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
1625 setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
1626 setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
1628 _add_node_class_names(node_class_names)
1631 class TreeCopyVisitor(GenericNodeVisitor):
1634 Make a complete copy of a tree or branch, including element attributes.
1637 def __init__(self, document):
1638 GenericNodeVisitor.__init__(self, document)
1639 self.parent_stack = []
1640 self.parent = []
1642 def get_tree_copy(self):
1643 return self.parent[0]
1645 def default_visit(self, node):
1646 """Copy the current node, and make it the new acting parent."""
1647 newnode = node.copy()
1648 self.parent.append(newnode)
1649 self.parent_stack.append(self.parent)
1650 self.parent = newnode
1652 def default_departure(self, node):
1653 """Restore the previous acting parent."""
1654 self.parent = self.parent_stack.pop()
1657 class TreePruningException(Exception):
1660 Base class for `NodeVisitor`-related tree pruning exceptions.
1662 Raise subclasses from within ``visit_...`` or ``depart_...`` methods
1663 called from `Node.walk()` and `Node.walkabout()` tree traversals to prune
1664 the tree traversed.
1667 pass
1670 class SkipChildren(TreePruningException):
1673 Do not visit any children of the current node. The current node's
1674 siblings and ``depart_...`` method are not affected.
1677 pass
1680 class SkipSiblings(TreePruningException):
1683 Do not visit any more siblings (to the right) of the current node. The
1684 current node's children and its ``depart_...`` method are not affected.
1687 pass
1690 class SkipNode(TreePruningException):
1693 Do not visit the current node's children, and do not call the current
1694 node's ``depart_...`` method.
1697 pass
1700 class SkipDeparture(TreePruningException):
1703 Do not call the current node's ``depart_...`` method. The current node's
1704 children and siblings are not affected.
1707 pass
1710 class NodeFound(TreePruningException):
1713 Raise to indicate that the target of a search has been found. This
1714 exception must be caught by the client; it is not caught by the traversal
1715 code.
1718 pass
1721 class StopTraversal(TreePruningException):
1724 Stop the traversal alltogether. The current node's ``depart_...`` method
1725 is not affected. The parent nodes ``depart_...`` methods are also called
1726 as usual. No other nodes are visited. This is an alternative to
1727 NodeFound that does not cause exception handling to trickle up to the
1728 caller.
1731 pass
1734 def make_id(string):
1736 Convert `string` into an identifier and return it.
1738 Docutils identifiers will conform to the regular expression
1739 ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class"
1740 and "id" attributes) should have no underscores, colons, or periods.
1741 Hyphens may be used.
1743 - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
1745 ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
1746 followed by any number of letters, digits ([0-9]), hyphens ("-"),
1747 underscores ("_"), colons (":"), and periods (".").
1749 - However the `CSS1 spec`_ defines identifiers based on the "name" token,
1750 a tighter interpretation ("flex" tokenizer notation; "latin1" and
1751 "escape" 8-bit characters have been replaced with entities)::
1753 unicode \\[0-9a-f]{1,4}
1754 latin1 [&iexcl;-&yuml;]
1755 escape {unicode}|\\[ -~&iexcl;-&yuml;]
1756 nmchar [-a-z0-9]|{latin1}|{escape}
1757 name {nmchar}+
1759 The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"),
1760 or periods ("."), therefore "class" and "id" attributes should not contain
1761 these characters. They should be replaced with hyphens ("-"). Combined
1762 with HTML's requirements (the first character must be a letter; no
1763 "unicode", "latin1", or "escape" characters), this results in the
1764 ``[a-z](-?[a-z0-9]+)*`` pattern.
1766 .. _HTML 4.01 spec: http://www.w3.org/TR/html401
1767 .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
1769 id = _non_id_chars.sub('-', ' '.join(string.lower().split()))
1770 id = _non_id_at_ends.sub('', id)
1771 return str(id)
1773 _non_id_chars = re.compile('[^a-z0-9]+')
1774 _non_id_at_ends = re.compile('^[-0-9]+|-+$')
1776 def dupname(node, name):
1777 node['dupnames'].append(name)
1778 node['names'].remove(name)
1779 # Assume that this method is referenced, even though it isn't; we
1780 # don't want to throw unnecessary system_messages.
1781 node.referenced = 1
1783 def fully_normalize_name(name):
1784 """Return a case- and whitespace-normalized name."""
1785 return ' '.join(name.lower().split())
1787 def whitespace_normalize_name(name):
1788 """Return a whitespace-normalized name."""
1789 return ' '.join(name.split())
1791 def serial_escape(value):
1792 """Escape string values that are elements of a list, for serialization."""
1793 return value.replace('\\', r'\\').replace(' ', r'\ ')
1795 # \f
1797 # Local Variables:
1798 # indent-tabs-mode: nil
1799 # sentence-end-double-space: t
1800 # fill-column: 78
1801 # End: