2 # Author: David Goodger <goodger@python.org>
3 # Copyright: This module has been placed in the public domain.
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
19 .. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
22 __docformat__
= 'reStructuredText'
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 # ==============================
39 """Abstract base class of nodes in a document tree."""
42 """Back-reference to the Node immediately containing this Node."""
45 """The `document` node at the root of the tree containing this Node."""
48 """Path or description of the input source which generated this Node."""
51 """The line number (1-based) of the beginning of this Node in `source`."""
53 def __nonzero__(self
):
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
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."""
74 import xml
.dom
.minidom
as dom
75 domroot
= dom
.Document()
76 return self
._dom
_node
(domroot
)
78 def pformat(self
, indent
=' ', level
=0):
80 Return an indented pseudo-XML representation, for test purposes.
82 Override in subclasses.
84 raise NotImplementedError
87 """Return a copy of self."""
88 raise NotImplementedError
91 """Return a deep copy of self (also copying children)."""
92 raise NotImplementedError
94 def setup_child(self
, child
):
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
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.
127 visitor
.document
.reporter
.debug(
128 'docutils.nodes.Node.walk calling dispatch_visit for %s'
129 % self
.__class
__.__name
__)
132 visitor
.dispatch_visit(self
)
133 except (SkipChildren
, SkipNode
):
135 except SkipDeparture
: # not applicable; ignore
137 children
= self
.children
139 for child
in children
[:]:
140 if child
.walk(visitor
):
145 except StopTraversal
:
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.
163 visitor
.document
.reporter
.debug(
164 'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
165 % self
.__class
__.__name
__)
168 visitor
.dispatch_visit(self
)
171 except SkipDeparture
:
173 children
= self
.children
175 for child
in children
[:]:
176 if child
.walkabout(visitor
):
183 except StopTraversal
:
186 visitor
.document
.reporter
.debug(
187 'docutils.nodes.Node.walkabout calling dispatch_departure '
188 'for %s' % self
.__class
__.__name
__)
189 visitor
.dispatch_departure(self
)
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::
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>]
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
)):
240 if descend
and len(self
.children
):
242 r
.extend(child
.traverse(
243 include_self
=1, descend
=1, siblings
=0, ascend
=0,
244 condition
=condition
))
245 if siblings
or ascend
:
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
))
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
)
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.
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."""
296 data
= repr(self
.data
)
298 data
= repr(self
.data
[:64] + ' ...')
299 return '<%s: %s>' % (self
.tagname
, data
)
302 return len(self
.data
)
305 data
= repr(self
.data
)
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
)
316 def __unicode__(self
):
320 return self
.__class
__(self
.data
)
325 def pformat(self
, indent
=' ', level
=0):
327 indent
= indent
* level
328 for line
in self
.data
.splitlines():
329 result
.append(indent
+ line
+ '\n')
330 return ''.join(result
)
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::
355 Elements may be constructed using the ``+=`` operator. To add one new
356 child node to element, do::
360 This is equivalent to ``element.append(node)``.
362 To add a list of multiple child nodes at once, use the same ``+=``
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
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."""
386 """List of child nodes (elements and/or `Text`)."""
388 self
.extend(children
) # maintain parent info
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():
399 if att
in self
.list_attributes
:
400 # mutable list; make a copy for this node
401 self
.attributes
[att
] = value
[:]
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
))
420 for c
in self
.children
:
421 data
+= c
.shortrepr()
423 data
= data
[:56] + ' ...'
426 return '<%s "%s": %s>' % (self
.__class
__.__name
__,
427 '; '.join(self
['names']), data
)
429 return '<%s: %s>' % (self
.__class
__.__name
__, data
)
433 return '<%s "%s"...>' % (self
.__class
__.__name
__,
434 '; '.join(self
['names']))
436 return '<%s...>' % self
.tagname
438 def __unicode__(self
):
440 return u
'%s%s%s' % (self
.starttag(),
441 ''.join([unicode(c
) for c
in self
.children
]),
444 return self
.emptytag()
447 parts
= [self
.tagname
]
448 for name
, value
in self
.attlist():
449 if value
is None: # boolean attribute
451 elif isinstance(value
, ListType
):
452 values
= [serial_escape('%s' % v
) for v
in value
]
453 parts
.append('%s="%s"' % (name
, ' '.join(values
)))
455 parts
.append('%s="%s"' % (name
, value
))
456 return '<%s>' % ' '.join(parts
)
459 return '</%s>' % self
.tagname
462 return u
'<%s/>' % ' '.join([self
.tagname
] +
464 for n
, v
in self
.attlist()])
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
]
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'
490 self
.setup_child(node
)
491 self
.children
[key
.start
:key
.stop
] = item
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
]
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
):
518 elif other
is not None:
523 return self
.child_text_separator
.join(
524 [child
.astext() for child
in self
.children
])
526 def non_default_attributes(self
):
528 for key
, value
in self
.attributes
.items():
529 if self
.is_not_default(key
):
534 attlist
= self
.non_default_attributes().items()
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
)
553 def append(self
, item
):
554 self
.setup_child(item
)
555 self
.children
.append(item
)
557 def extend(self
, item
):
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
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
:
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
)
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
)
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
613 if not isinstance(new
, Node
):
614 # `new` is a list; update first child.
619 if isinstance(update
, Element
):
620 update
.update_basic_atts(self
)
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.
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
)):
644 if isinstance(self
[index
], c
):
648 def first_child_not_matching_class(self
, childclass
, start
=0,
651 Return the index of the first child whose class does *not* match.
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
)):
664 if isinstance(self
.children
[index
], c
):
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
])
676 return self
.__class
__(**self
.attributes
)
680 copy
.extend([child
.deepcopy() for child
in self
.children
])
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`."""
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)
702 assert name
is not None
703 by_name
.referenced
= 1
705 assert id is not None
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
):
729 textnode
= Text(text
)
730 Element
.__init
__(self
, rawsource
, textnode
, *children
,
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'
756 def add_backref(self
, refid
):
757 self
['backrefs'].append(refid
)
760 # ====================
762 # ====================
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
779 class General(Body
): pass
781 class Sequential(Body
):
782 """List-like elements."""
784 class Admonition(Body
): pass
787 """Special internal body elements."""
789 class Invisible(PreBibliographic
):
790 """Internal elements that don't appear in output."""
796 class Referential(Resolvable
): pass
799 class Targetable(Resolvable
):
803 indirect_reference_name
= None
804 """Holds the whitespace_normalized_name (contains mixed case) of a target.
805 Required for MoinMoin/reST compatibility."""
809 """Contains a `label` as its first element."""
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
851 """Mapping of names to lists of referencing nodes."""
854 """Mapping of ids to lists of referencing nodes."""
857 """Mapping of names to unique id's."""
860 """Mapping of names to hyperlink type (boolean: True => explicit,
861 False => implicit."""
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."""
885 """List of manually-numbered footnote nodes."""
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."""
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."""
914 def __getstate__(self
):
916 Return dict with unpicklable references removed.
918 state
= self
.__dict
__.copy()
919 state
['reporter'] = None
920 state
['transformer'] = None
923 def asdom(self
, dom
=None):
924 """Return a DOM representation of this document."""
926 import xml
.dom
.minidom
as dom
927 domroot
= dom
.Document()
928 domroot
.appendChild(self
._dom
_node
(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)
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):
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
))
948 node
['ids'].append(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
)
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
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
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
)
1016 self
.nameids
[name
] = id
1017 if old_id
is not None:
1018 old_node
= self
.ids
[old_id
]
1019 dupname(old_node
, name
)
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
)
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
)
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
)
1054 self
.note_refname(target
)
1056 def note_anonymous_target(self
, target
):
1059 def note_autofootnote(self
, footnote
):
1060 self
.set_id(footnote
)
1061 self
.autofootnotes
.append(footnote
)
1063 def note_autofootnote_ref(self
, 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
):
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
):
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
):
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
,
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
1122 self
.current_line
= offset
1124 self
.current_line
= offset
+ 1
1127 return self
.__class
__(self
.settings
, self
.reporter
,
1130 def get_decoration(self
):
1131 if not self
.decoration
:
1132 self
.decoration
= decoration()
1133 index
= self
.first_child_not_matching_class(Titular
)
1135 self
.append(self
.decoration
)
1137 self
.insert(index
, self
.decoration
)
1138 return self
.decoration
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
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
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
):
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
):
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
):
1327 p
= paragraph('', message
)
1328 children
= (p
,) + children
1330 Element
.__init
__(self
, '', *children
, **attributes
)
1332 print 'system_message: children=%r' % (children
,)
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::
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
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
1377 self
.details
= details
or {}
1378 """Detail data (dictionary) required by the pending operation."""
1380 def pformat(self
, indent
=' ', level
=0):
1382 '.. internal attributes:',
1383 ' .transform: %s.%s' % (self
.transform
.__module
__,
1384 self
.transform
.__name
__),
1386 details
= self
.details
.items()
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
))
1397 internals
.extend(['%9s%s' % ('', line
)
1398 for line
in v
.pformat().splitlines()])
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
]))
1406 return self
.__class
__(self
.transform
, self
.details
, self
.rawsource
,
1410 class raw(Special
, Inline
, PreBibliographic
, FixedTextElement
):
1413 Raw data that is to be passed untouched to the Writer.
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
):
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
= """
1454 abbreviation acronym address admonition attention attribution author
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
1466 image important inline
1467 label legend line line_block list_item literal literal_block
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
1479 """A list of names of all concrete Node subclasses."""
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
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,
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
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
))
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
))
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
):
1620 def _add_node_class_names(names
):
1621 """Save typing with dynamic assignments:"""
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
= []
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
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.
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.
1690 class SkipNode(TreePruningException
):
1693 Do not visit the current node's children, and do not call the current
1694 node's ``depart_...`` method.
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.
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
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
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 [¡-ÿ]
1755 escape {unicode}|\\[ -~¡-ÿ]
1756 nmchar [-a-z0-9]|{latin1}|{escape}
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)
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.
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
'\ ')
1798 # indent-tabs-mode: nil
1799 # sentence-end-double-space: t