66
def asdom(self, dom=xml.dom.minidom):
66
def asdom(self, dom=None):
67
67
"""Return a DOM **fragment** representation of this Node."""
69
import xml.dom.minidom as dom
68
70
domroot = dom.Document()
69
71
return self._dom_node(domroot)
71
73
def pformat(self, indent=' ', level=0):
72
"""Return an indented pseudo-XML representation, for test purposes."""
75
Return an indented pseudo-XML representation, for test purposes.
77
Override in subclasses.
73
79
raise NotImplementedError
76
82
"""Return a copy of self."""
77
83
raise NotImplementedError
86
"""Return a deep copy of self (also copying children)."""
87
raise NotImplementedError
79
89
def setup_child(self, child):
80
90
child.parent = self
88
98
def walk(self, visitor):
90
Traverse a tree of `Node` objects, calling ``visit_...`` methods of
91
`visitor` when entering each node. If there is no
92
``visit_particular_node`` method for a node of type
93
``particular_node``, the ``unknown_visit`` method is called. (The
94
`walkabout()` method is similar, except it also calls ``depart_...``
95
methods before exiting each node.)
100
Traverse a tree of `Node` objects, calling the
101
`dispatch_visit()` method of `visitor` when entering each
102
node. (The `walkabout()` method is similar, except it also
103
calls the `dispatch_departure()` method before exiting each
97
106
This tree traversal supports limited in-place tree
98
107
modifications. Replacing one node with one or more nodes is
100
109
or replaced occurs after the current node, the old node will
101
110
still be traversed, and any new nodes will not.
103
Within ``visit_...`` methods (and ``depart_...`` methods for
112
Within ``visit`` methods (and ``depart`` methods for
104
113
`walkabout()`), `TreePruningException` subclasses may be raised
105
114
(`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
107
116
Parameter `visitor`: A `NodeVisitor` object, containing a
108
``visit_...`` method for each `Node` subclass encountered.
117
``visit`` implementation for each `Node` subclass encountered.
110
name = 'visit_' + self.__class__.__name__
111
method = getattr(visitor, name, visitor.unknown_visit)
112
visitor.document.reporter.debug(name, category='nodes.Node.walk')
119
visitor.document.reporter.debug(
120
'docutils.nodes.Node.walk calling dispatch_visit for %s'
121
% self.__class__.__name__)
123
visitor.dispatch_visit(self)
115
124
except (SkipChildren, SkipNode):
117
126
except SkipDeparture: # not applicable; ignore
119
children = self.get_children()
128
children = self.children
121
130
for child in children[:]:
122
131
child.walk(visitor)
126
135
def walkabout(self, visitor):
128
Perform a tree traversal similarly to `Node.walk()` (which see),
129
except also call ``depart_...`` methods before exiting each node. If
130
there is no ``depart_particular_node`` method for a node of type
131
``particular_node``, the ``unknown_departure`` method is called.
137
Perform a tree traversal similarly to `Node.walk()` (which
138
see), except also call the `dispatch_departure()` method
139
before exiting each node.
133
Parameter `visitor`: A `NodeVisitor` object, containing ``visit_...``
134
and ``depart_...`` methods for each `Node` subclass encountered.
141
Parameter `visitor`: A `NodeVisitor` object, containing a
142
``visit`` and ``depart`` implementation for each `Node`
143
subclass encountered.
137
name = 'visit_' + self.__class__.__name__
138
method = getattr(visitor, name, visitor.unknown_visit)
139
visitor.document.reporter.debug(name, category='nodes.Node.walkabout')
146
visitor.document.reporter.debug(
147
'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
148
% self.__class__.__name__)
151
visitor.dispatch_visit(self)
145
154
except SkipDeparture:
147
children = self.get_children()
156
children = self.children
149
158
for child in children[:]:
150
159
child.walkabout(visitor)
153
162
except SkipChildren:
156
name = 'depart_' + self.__class__.__name__
157
method = getattr(visitor, name, visitor.unknown_departure)
158
165
visitor.document.reporter.debug(
159
name, category='nodes.Node.walkabout')
166
'docutils.nodes.Node.walkabout calling dispatch_departure '
167
'for %s' % self.__class__.__name__)
168
visitor.dispatch_departure(self)
170
def traverse(self, condition=None,
171
include_self=1, descend=1, siblings=0, ascend=0):
173
Return an iterable containing
175
* self (if include_self is true)
176
* all descendants in tree traversal order (if descend is true)
177
* all siblings (if siblings is true) and their descendants (if
178
also descend is true)
179
* the siblings of the parent (if ascend is true) and their
180
descendants (if also descend is true), and so on
182
If `condition` is not None, the iterable contains only nodes
183
for which ``condition(node)`` is true. If `condition` is a
184
node class ``cls``, it is equivalent to a function consisting
185
of ``return isinstance(node, cls)``.
187
If ascend is true, assume siblings to be true as well.
189
For example, given the following tree::
192
<emphasis> <--- emphasis.traverse() and
193
<strong> <--- strong.traverse() are called.
196
<reference name="Baz" refid="baz">
199
Then list(emphasis.traverse()) equals ::
201
[<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
203
and list(strong.traverse(ascend=1)) equals ::
205
[<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
210
if isinstance(condition, ClassType):
211
node_class = condition
212
def condition(node, node_class=node_class):
213
return isinstance(node, node_class)
214
if include_self and (condition is None or condition(self)):
216
if descend and len(self.children):
218
r.extend(child.traverse(
219
include_self=1, descend=1, siblings=0, ascend=0,
220
condition=condition))
221
if siblings or ascend:
224
index = node.parent.index(node)
225
for sibling in node.parent[index+1:]:
226
r.extend(sibling.traverse(include_self=1, descend=descend,
227
siblings=0, ascend=0,
228
condition=condition))
235
def next_node(self, condition=None,
236
include_self=0, descend=1, siblings=0, ascend=0):
238
Return the first node in the iterable returned by traverse(),
239
or None if the iterable is empty.
241
Parameter list is the same as of traverse. Note that
242
include_self defaults to 0, though.
244
iterable = self.traverse(condition=condition,
245
include_self=include_self, descend=descend,
246
siblings=siblings, ascend=ascend)
163
252
class Text(Node, UserString):
262
363
self.attributes = {}
263
364
"""Dictionary of attribute {name: value}."""
366
# Initialize list attributes.
367
for att in self.list_attributes:
368
self.attributes[att] = []
265
370
for att, value in attributes.items():
266
self.attributes[att.lower()] = value
372
if att in self.list_attributes:
373
# mutable list; make a copy for this node
374
self.attributes[att] = value[:]
376
self.attributes[att] = value
268
378
if self.tagname is None:
269
379
self.tagname = self.__class__.__name__
271
381
def _dom_node(self, domroot):
272
382
element = domroot.createElement(self.tagname)
273
for attribute, value in self.attributes.items():
383
for attribute, value in self.attlist():
274
384
if isinstance(value, ListType):
275
value = ' '.join(['%s' % v for v in value])
385
value = ' '.join([serial_escape('%s' % v) for v in value])
276
386
element.setAttribute(attribute, '%s' % value)
277
387
for child in self.children:
278
388
element.appendChild(child._dom_node(domroot))
285
395
if len(data) > 60:
286
396
data = data[:56] + ' ...'
288
if self.hasattr('name'):
289
399
return '<%s "%s": %s>' % (self.__class__.__name__,
290
self.attributes['name'], data)
400
'; '.join(self['names']), data)
292
402
return '<%s: %s>' % (self.__class__.__name__, data)
294
404
def shortrepr(self):
295
if self.hasattr('name'):
296
406
return '<%s "%s"...>' % (self.__class__.__name__,
297
self.attributes['name'])
407
'; '.join(self['names']))
299
409
return '<%s...>' % self.tagname
380
490
def __iadd__(self, other):
381
491
"""Append a node or a list of nodes to `self.children`."""
382
492
if isinstance(other, Node):
383
self.setup_child(other)
384
self.children.append(other)
385
494
elif other is not None:
387
self.setup_child(node)
388
self.children.extend(other)
391
498
def astext(self):
392
499
return self.child_text_separator.join(
393
500
[child.astext() for child in self.children])
502
def non_default_attributes(self):
504
for key, value in self.attributes.items():
505
if self.is_not_default(key):
395
509
def attlist(self):
396
attlist = self.attributes.items()
510
attlist = self.non_default_attributes().items()
437
550
def index(self, item):
438
551
return self.children.index(item)
553
def is_not_default(self, key):
554
if self[key] == [] and key in self.list_attributes:
559
def update_basic_atts(self, dict):
561
Update basic attributes ('ids', 'names', 'classes',
562
'dupnames', but not 'source') from node or dictionary `dict`.
564
if isinstance(dict, Node):
565
dict = dict.attributes
566
for att in ('ids', 'classes', 'names', 'dupnames'):
567
for value in dict.get(att, []):
568
if not value in self[att]:
569
self[att].append(value)
440
574
def replace(self, old, new):
441
575
"""Replace one child `Node` with another child or children."""
442
576
index = self.index(old)
446
580
elif new is not None:
447
581
self[index:index+1] = new
583
def replace_self(self, new):
585
Replace `self` node with `new`, where `new` is a node or a
589
if not isinstance(new, Node):
590
# `new` is a list; update first child.
595
if isinstance(update, Element):
596
update.update_basic_atts(self)
598
# `update` is a Text node or `new` is an empty list.
599
# Assert that we aren't losing any attributes.
600
for att in ('ids', 'names', 'classes', 'dupnames'):
601
assert not self[att], \
602
'Losing "%s" attribute: %s' % (att, self[att])
603
self.parent.replace(self, new)
449
605
def first_child_matching_class(self, childclass, start=0, end=sys.maxint):
451
607
Return the index of the first child whose class exactly matches.
494
648
[child.pformat(indent, level+1)
495
649
for child in self.children])
497
def get_children(self):
498
"""Return this element's children."""
502
652
return self.__class__(**self.attributes)
656
copy.extend([child.deepcopy() for child in self.children])
504
659
def set_class(self, name):
505
"""Add a new name to the "class" attribute."""
506
self.attributes['class'] = (self.attributes.get('class', '') + ' '
507
+ name.lower()).strip()
660
"""Add a new class to the "classes" attribute."""
661
warnings.warn('docutils.nodes.Element.set_class deprecated; '
662
"append to Element['classes'] list attribute directly",
663
DeprecationWarning, stacklevel=2)
664
assert ' ' not in name
665
self['classes'].append(name.lower())
667
def note_referenced_by(self, name=None, id=None):
668
"""Note that this Element has been referenced by its name
669
`name` or id `id`."""
671
# Element.expect_referenced_by_* dictionaries map names or ids
672
# to nodes whose ``referenced`` attribute is set to true as
673
# soon as this node is referenced by the given name or id.
674
# Needed for target propagation.
675
by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
676
by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
678
assert name is not None
679
by_name.referenced = 1
681
assert id is not None
510
685
class TextElement(Element):
715
882
self.transformer = docutils.transforms.Transformer(self)
716
883
"""Storage for transforms to be applied to this document."""
885
self.decoration = None
886
"""Document's `decoration` node."""
718
888
self.document = self
720
def asdom(self, dom=xml.dom.minidom):
890
def asdom(self, dom=None):
721
891
"""Return a DOM representation of this document."""
893
import xml.dom.minidom as dom
722
894
domroot = dom.Document()
723
895
domroot.appendChild(self._dom_node(domroot))
726
898
def set_id(self, node, msgnode=None):
727
if node.has_key('id'):
899
for id in node['ids']:
729
900
if self.ids.has_key(id) and self.ids[id] is not node:
730
901
msg = self.reporter.severe('Duplicate ID: "%s".' % id)
731
902
if msgnode != None:
734
if node.has_key('name'):
735
id = make_id(node['name'])
905
for name in node['names']:
906
id = self.settings.id_prefix + make_id(name)
907
if id and not self.ids.has_key(id):
738
while not id or self.ids.has_key(id):
739
id = 'id%s' % self.id_start
911
while not id or self.ids.has_key(id):
912
id = (self.settings.id_prefix +
913
self.settings.auto_id_prefix + str(self.id_start))
915
node['ids'].append(id)
742
916
self.ids[id] = node
751
925
The following state transition table shows how `self.nameids` ("ids")
752
926
and `self.nametypes` ("types") change with new input (a call to this
753
method), and what actions are performed:
927
method), and what actions are performed ("implicit"-type system
928
messages are INFO/1, and "explicit"-type system messages are ERROR/3):
755
930
==== ===== ======== ======== ======= ==== ===== =====
756
931
Old State Input Action New State Notes
757
932
----------- -------- ----------------- ----------- -----
758
933
ids types new type sys.msg. dupname ids types
759
934
==== ===== ======== ======== ======= ==== ===== =====
760
-- -- explicit -- -- new True
761
-- -- implicit -- -- new False
762
None False explicit -- -- new True
935
- - explicit - - new True
936
- - implicit - - new False
937
None False explicit - - new True
763
938
old False explicit implicit old new True
764
939
None True explicit explicit new None True
765
940
old True explicit explicit new,old None True [#]_
792
966
old_node = self.ids[old_id]
793
967
if node.has_key('refuri'):
794
968
refuri = node['refuri']
795
if old_node.has_key('name') \
969
if old_node['names'] \
796
970
and old_node.has_key('refuri') \
797
971
and old_node['refuri'] == refuri:
798
972
level = 1 # just inform if refuri's identical
974
dupname(old_node, name)
801
975
self.nameids[name] = None
802
976
msg = self.reporter.system_message(
803
977
level, 'Duplicate explicit target name: "%s".' % name,
804
978
backrefs=[id], base_node=node)
805
979
if msgnode != None:
809
983
self.nameids[name] = id
810
984
if old_id is not None:
811
985
old_node = self.ids[old_id]
986
dupname(old_node, name)
814
988
if old_id is not None and not old_explicit:
815
989
self.nameids[name] = None
816
990
old_node = self.ids[old_id]
991
dupname(old_node, name)
819
993
if not explicit or (not old_explicit and old_id is not None):
820
994
msg = self.reporter.info(
821
995
'Duplicate implicit target name: "%s".' % name,
841
1015
def note_refid(self, node):
842
1016
self.refids.setdefault(node['refid'], []).append(node)
844
def note_external_target(self, target):
845
self.external_targets.append(target)
847
def note_internal_target(self, target):
848
self.internal_targets.append(target)
850
1018
def note_indirect_target(self, target):
851
1019
self.indirect_targets.append(target)
852
if target.has_key('name'):
853
1021
self.note_refname(target)
855
1023
def note_anonymous_target(self, target):
856
1024
self.set_id(target)
857
self.anonymous_targets.append(target)
859
def note_anonymous_ref(self, ref):
860
self.anonymous_refs.append(ref)
862
1026
def note_autofootnote(self, footnote):
863
1027
self.set_id(footnote)
901
1065
if msgnode != None:
903
1067
oldnode = self.substitution_defs[name]
1068
dupname(oldnode, name)
905
1069
# keep only the last definition:
906
1070
self.substitution_defs[name] = subdef
907
1071
# case-insensitive mapping:
908
1072
self.substitution_names[fully_normalize_name(name)] = name
910
1074
def note_substitution_ref(self, subref, refname):
911
name = subref['refname'] = whitespace_normalize_name(refname)
912
self.substitution_refs.setdefault(name, []).append(subref)
1075
subref['refname'] = whitespace_normalize_name(refname)
914
1077
def note_pending(self, pending, priority=None):
915
1078
self.transformer.add_pending(pending, priority)
1056
1242
class description(Part, Element): pass
1057
1243
class literal_block(General, FixedTextElement): pass
1058
1244
class doctest_block(General, FixedTextElement): pass
1059
class line_block(General, FixedTextElement): pass
1245
class line_block(General, Element): pass
1248
class line(Part, TextElement):
1060
1253
class block_quote(General, Element): pass
1061
1254
class attribution(Part, TextElement): pass
1062
1255
class attention(Admonition, Element): pass
1069
1262
class hint(Admonition, Element): pass
1070
1263
class warning(Admonition, Element): pass
1071
1264
class admonition(Admonition, Element): pass
1072
class comment(Special, Invisible, PreBibliographic, FixedTextElement): pass
1265
class comment(Special, Invisible, FixedTextElement): pass
1073
1266
class substitution_definition(Special, Invisible, TextElement): pass
1074
1267
class target(Special, Invisible, Inline, TextElement, Targetable): pass
1075
class footnote(General, Element, Labeled, BackLinkable): pass
1076
class citation(General, Element, Labeled, BackLinkable): pass
1268
class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
1269
class citation(General, BackLinkable, Element, Labeled, Targetable): pass
1077
1270
class label(Part, TextElement): pass
1078
1271
class figure(General, Element): pass
1079
1272
class caption(Part, TextElement): pass
1087
1280
class entry(Part, Element): pass
1090
class system_message(Special, PreBibliographic, Element, BackLinkable):
1283
class system_message(Special, BackLinkable, PreBibliographic, Element):
1286
System message element.
1288
Do not instantiate this class directly; use
1289
``document.reporter.info/warning/error/severe()`` instead.
1092
1292
def __init__(self, message=None, *children, **attributes):
1249
1449
class NodeVisitor:
1252
"Visitor" pattern [GoF95]_ abstract superclass implementation for document
1255
Each node class has corresponding methods, doing nothing by default;
1256
override individual methods for specific and useful behaviour. The
1257
"``visit_`` + node class name" method is called by `Node.walk()` upon
1258
entering a node. `Node.walkabout()` also calls the "``depart_`` + node
1259
class name" method before exiting a node.
1452
"Visitor" pattern [GoF95]_ abstract superclass implementation for
1453
document tree traversals.
1455
Each node class has corresponding methods, doing nothing by
1456
default; override individual methods for specific and useful
1457
behaviour. The `dispatch_visit()` method is called by
1458
`Node.walk()` upon entering a node. `Node.walkabout()` also calls
1459
the `dispatch_departure()` method before exiting a node.
1461
The dispatch methods call "``visit_`` + node class name" or
1462
"``depart_`` + node class name", resp.
1261
1464
This is a base class for visitors whose ``visit_...`` & ``depart_...``
1262
1465
methods should be implemented for *all* node types encountered (such as
1480
Tuple containing node class names (as strings).
1482
No exception will be raised if writers do not implement visit
1483
or departure functions for these node classes.
1485
Used to ensure transitional compatibility with existing 3rd-party writers.
1275
1488
def __init__(self, document):
1276
1489
self.document = document
1491
def dispatch_visit(self, node):
1493
Call self."``visit_`` + node class name" with `node` as
1494
parameter. If the ``visit_...`` method does not exist, call
1497
node_name = node.__class__.__name__
1498
method = getattr(self, 'visit_' + node_name, self.unknown_visit)
1499
self.document.reporter.debug(
1500
'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
1501
% (method.__name__, node_name))
1504
def dispatch_departure(self, node):
1506
Call self."``depart_`` + node class name" with `node` as
1507
parameter. If the ``depart_...`` method does not exist, call
1508
self.unknown_departure.
1510
node_name = node.__class__.__name__
1511
method = getattr(self, 'depart_' + node_name, self.unknown_departure)
1512
self.document.reporter.debug(
1513
'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
1514
% (method.__name__, node_name))
1278
1517
def unknown_visit(self, node):
1280
1519
Called when entering unknown `Node` types.
1282
1521
Raise an exception unless overridden.
1284
raise NotImplementedError('%s visiting unknown node type: %s'
1285
% (self.__class__, node.__class__.__name__))
1523
if (node.document.settings.strict_visitor
1524
or node.__class__.__name__ not in self.optional):
1525
raise NotImplementedError(
1526
'%s visiting unknown node type: %s'
1527
% (self.__class__, node.__class__.__name__))
1287
1529
def unknown_departure(self, node):
1291
1533
Raise exception unless overridden.
1293
raise NotImplementedError('%s departing unknown node type: %s'
1294
% (self.__class__, node.__class__.__name__))
1535
if (node.document.settings.strict_visitor
1536
or node.__class__.__name__ not in self.optional):
1537
raise NotImplementedError(
1538
'%s departing unknown node type: %s'
1539
% (self.__class__, node.__class__.__name__))
1297
1542
class SparseNodeVisitor(NodeVisitor):
1344
1590
setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
1345
1591
setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
1346
1592
setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
1347
setattr(SparseNodeVisitor, 'depart' + _name, _nop)
1593
setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
1349
1595
_add_node_class_names(node_class_names)
1351
1598
class TreeCopyVisitor(GenericNodeVisitor):