Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/docutils/nodes.py: 69%

961 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:06 +0000

1# $Id$ 

2# Author: David Goodger <goodger@python.org> 

3# Maintainer: docutils-develop@lists.sourceforge.net 

4# Copyright: This module has been placed in the public domain. 

5 

6""" 

7Docutils document tree element class library. 

8 

9Classes in CamelCase are abstract base classes or auxiliary classes. The one 

10exception is `Text`, for a text (PCDATA) node; uppercase is used to 

11differentiate from element classes. Classes in lower_case_with_underscores 

12are element classes, matching the XML element generic identifiers in the DTD_. 

13 

14The position of each node (the level at which it can occur) is significant and 

15is represented by abstract base classes (`Root`, `Structural`, `Body`, 

16`Inline`, etc.). Certain transformations will be easier because we can use 

17``isinstance(node, base_class)`` to determine the position of the node in the 

18hierarchy. 

19 

20.. _DTD: https://docutils.sourceforge.io/docs/ref/docutils.dtd 

21""" 

22 

23__docformat__ = 'reStructuredText' 

24 

25from collections import Counter 

26import re 

27import sys 

28import warnings 

29import unicodedata 

30# import xml.dom.minidom as dom # -> conditional import in Node.asdom() 

31# and document.asdom() 

32 

33# import docutils.transforms # -> conditional import in document.__init__() 

34 

35 

36# ============================== 

37# Functional Node Base Classes 

38# ============================== 

39 

40class Node: 

41 """Abstract base class of nodes in a document tree.""" 

42 

43 parent = None 

44 """Back-reference to the Node immediately containing this Node.""" 

45 

46 source = None 

47 """Path or description of the input source which generated this Node.""" 

48 

49 line = None 

50 """The line number (1-based) of the beginning of this Node in `source`.""" 

51 

52 _document = None 

53 

54 @property 

55 def document(self): 

56 """Return the `document` root node of the tree containing this Node. 

57 """ 

58 try: 

59 return self._document or self.parent.document 

60 except AttributeError: 

61 return None 

62 

63 @document.setter 

64 def document(self, value): 

65 self._document = value 

66 

67 def __bool__(self): 

68 """ 

69 Node instances are always true, even if they're empty. A node is more 

70 than a simple container. Its boolean "truth" does not depend on 

71 having one or more subnodes in the doctree. 

72 

73 Use `len()` to check node length. 

74 """ 

75 return True 

76 

77 def asdom(self, dom=None): 

78 """Return a DOM **fragment** representation of this Node.""" 

79 if dom is None: 

80 import xml.dom.minidom as dom 

81 domroot = dom.Document() 

82 return self._dom_node(domroot) 

83 

84 def pformat(self, indent=' ', level=0): 

85 """ 

86 Return an indented pseudo-XML representation, for test purposes. 

87 

88 Override in subclasses. 

89 """ 

90 raise NotImplementedError 

91 

92 def copy(self): 

93 """Return a copy of self.""" 

94 raise NotImplementedError 

95 

96 def deepcopy(self): 

97 """Return a deep copy of self (also copying children).""" 

98 raise NotImplementedError 

99 

100 def astext(self): 

101 """Return a string representation of this Node.""" 

102 raise NotImplementedError 

103 

104 def setup_child(self, child): 

105 child.parent = self 

106 if self.document: 

107 child.document = self.document 

108 if child.source is None: 

109 child.source = self.document.current_source 

110 if child.line is None: 

111 child.line = self.document.current_line 

112 

113 def walk(self, visitor): 

114 """ 

115 Traverse a tree of `Node` objects, calling the 

116 `dispatch_visit()` method of `visitor` when entering each 

117 node. (The `walkabout()` method is similar, except it also 

118 calls the `dispatch_departure()` method before exiting each 

119 node.) 

120 

121 This tree traversal supports limited in-place tree 

122 modifications. Replacing one node with one or more nodes is 

123 OK, as is removing an element. However, if the node removed 

124 or replaced occurs after the current node, the old node will 

125 still be traversed, and any new nodes will not. 

126 

127 Within ``visit`` methods (and ``depart`` methods for 

128 `walkabout()`), `TreePruningException` subclasses may be raised 

129 (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`). 

130 

131 Parameter `visitor`: A `NodeVisitor` object, containing a 

132 ``visit`` implementation for each `Node` subclass encountered. 

133 

134 Return true if we should stop the traversal. 

135 """ 

136 stop = False 

137 visitor.document.reporter.debug( 

138 'docutils.nodes.Node.walk calling dispatch_visit for %s' 

139 % self.__class__.__name__) 

140 try: 

141 try: 

142 visitor.dispatch_visit(self) 

143 except (SkipChildren, SkipNode): 

144 return stop 

145 except SkipDeparture: # not applicable; ignore 

146 pass 

147 children = self.children 

148 try: 

149 for child in children[:]: 

150 if child.walk(visitor): 

151 stop = True 

152 break 

153 except SkipSiblings: 

154 pass 

155 except StopTraversal: 

156 stop = True 

157 return stop 

158 

159 def walkabout(self, visitor): 

160 """ 

161 Perform a tree traversal similarly to `Node.walk()` (which 

162 see), except also call the `dispatch_departure()` method 

163 before exiting each node. 

164 

165 Parameter `visitor`: A `NodeVisitor` object, containing a 

166 ``visit`` and ``depart`` implementation for each `Node` 

167 subclass encountered. 

168 

169 Return true if we should stop the traversal. 

170 """ 

171 call_depart = True 

172 stop = False 

173 visitor.document.reporter.debug( 

174 'docutils.nodes.Node.walkabout calling dispatch_visit for %s' 

175 % self.__class__.__name__) 

176 try: 

177 try: 

178 visitor.dispatch_visit(self) 

179 except SkipNode: 

180 return stop 

181 except SkipDeparture: 

182 call_depart = False 

183 children = self.children 

184 try: 

185 for child in children[:]: 

186 if child.walkabout(visitor): 

187 stop = True 

188 break 

189 except SkipSiblings: 

190 pass 

191 except SkipChildren: 

192 pass 

193 except StopTraversal: 

194 stop = True 

195 if call_depart: 

196 visitor.document.reporter.debug( 

197 'docutils.nodes.Node.walkabout calling dispatch_departure ' 

198 'for %s' % self.__class__.__name__) 

199 visitor.dispatch_departure(self) 

200 return stop 

201 

202 def _fast_findall(self, cls): 

203 """Return iterator that only supports instance checks.""" 

204 if isinstance(self, cls): 

205 yield self 

206 for child in self.children: 

207 yield from child._fast_findall(cls) 

208 

209 def _superfast_findall(self): 

210 """Return iterator that doesn't check for a condition.""" 

211 # This is different from ``iter(self)`` implemented via 

212 # __getitem__() and __len__() in the Element subclass, 

213 # which yields only the direct children. 

214 yield self 

215 for child in self.children: 

216 yield from child._superfast_findall() 

217 

218 def traverse(self, condition=None, include_self=True, descend=True, 

219 siblings=False, ascend=False): 

220 """Return list of nodes following `self`. 

221 

222 For looping, Node.findall() is faster and more memory efficient. 

223 """ 

224 # traverse() may be eventually removed: 

225 warnings.warn('nodes.Node.traverse() is obsoleted by Node.findall().', 

226 PendingDeprecationWarning, stacklevel=2) 

227 return list(self.findall(condition, include_self, descend, 

228 siblings, ascend)) 

229 

230 def findall(self, condition=None, include_self=True, descend=True, 

231 siblings=False, ascend=False): 

232 """ 

233 Return an iterator yielding nodes following `self`: 

234 

235 * self (if `include_self` is true) 

236 * all descendants in tree traversal order (if `descend` is true) 

237 * the following siblings (if `siblings` is true) and their 

238 descendants (if also `descend` is true) 

239 * the following siblings of the parent (if `ascend` is true) and 

240 their descendants (if also `descend` is true), and so on. 

241 

242 If `condition` is not None, the iterator yields only nodes 

243 for which ``condition(node)`` is true. If `condition` is a 

244 node class ``cls``, it is equivalent to a function consisting 

245 of ``return isinstance(node, cls)``. 

246 

247 If `ascend` is true, assume `siblings` to be true as well. 

248 

249 If the tree structure is modified during iteration, the result 

250 is undefined. 

251 

252 For example, given the following tree:: 

253 

254 <paragraph> 

255 <emphasis> <--- emphasis.traverse() and 

256 <strong> <--- strong.traverse() are called. 

257 Foo 

258 Bar 

259 <reference name="Baz" refid="baz"> 

260 Baz 

261 

262 Then tuple(emphasis.traverse()) equals :: 

263 

264 (<emphasis>, <strong>, <#text: Foo>, <#text: Bar>) 

265 

266 and list(strong.traverse(ascend=True) equals :: 

267 

268 [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>] 

269 """ 

270 if ascend: 

271 siblings = True 

272 # Check for special argument combinations that allow using an 

273 # optimized version of traverse() 

274 if include_self and descend and not siblings: 

275 if condition is None: 

276 yield from self._superfast_findall() 

277 return 

278 elif isinstance(condition, type): 

279 yield from self._fast_findall(condition) 

280 return 

281 # Check if `condition` is a class (check for TypeType for Python 

282 # implementations that use only new-style classes, like PyPy). 

283 if isinstance(condition, type): 

284 node_class = condition 

285 

286 def condition(node, node_class=node_class): 

287 return isinstance(node, node_class) 

288 

289 if include_self and (condition is None or condition(self)): 

290 yield self 

291 if descend and len(self.children): 

292 for child in self: 

293 yield from child.findall(condition=condition, 

294 include_self=True, descend=True, 

295 siblings=False, ascend=False) 

296 if siblings or ascend: 

297 node = self 

298 while node.parent: 

299 index = node.parent.index(node) 

300 # extra check since Text nodes have value-equality 

301 while node.parent[index] is not node: 

302 index = node.parent.index(node, index + 1) 

303 for sibling in node.parent[index+1:]: 

304 yield from sibling.findall( 

305 condition=condition, 

306 include_self=True, descend=descend, 

307 siblings=False, ascend=False) 

308 if not ascend: 

309 break 

310 else: 

311 node = node.parent 

312 

313 def next_node(self, condition=None, include_self=False, descend=True, 

314 siblings=False, ascend=False): 

315 """ 

316 Return the first node in the iterator returned by findall(), 

317 or None if the iterable is empty. 

318 

319 Parameter list is the same as of traverse. Note that `include_self` 

320 defaults to False, though. 

321 """ 

322 try: 

323 return next(self.findall(condition, include_self, 

324 descend, siblings, ascend)) 

325 except StopIteration: 

326 return None 

327 

328 

329class reprunicode(str): 

330 """ 

331 Deprecated backwards compatibility stub. Use the standard `str` instead. 

332 """ 

333 def __init__(self, s): 

334 warnings.warn('nodes.reprunicode() is not required with Python 3' 

335 ' and will be removed in Docutils 0.21 or later.', 

336 DeprecationWarning, stacklevel=2) 

337 super().__init__() 

338 

339 

340def ensure_str(s): 

341 """ 

342 Deprecated backwards compatibility stub returning `s`. 

343 """ 

344 warnings.warn('nodes.ensure_str() is not required with Python 3' 

345 ' and will be removed in Docutils 0.21 or later.', 

346 DeprecationWarning, stacklevel=2) 

347 return s 

348 

349 

350# definition moved here from `utils` to avoid circular import dependency 

351def unescape(text, restore_backslashes=False, respect_whitespace=False): 

352 """ 

353 Return a string with nulls removed or restored to backslashes. 

354 Backslash-escaped spaces are also removed. 

355 """ 

356 # `respect_whitespace` is ignored (since introduction 2016-12-16) 

357 if restore_backslashes: 

358 return text.replace('\x00', '\\') 

359 else: 

360 for sep in ['\x00 ', '\x00\n', '\x00']: 

361 text = ''.join(text.split(sep)) 

362 return text 

363 

364 

365class Text(Node, str): 

366 

367 """ 

368 Instances are terminal nodes (leaves) containing text only; no child 

369 nodes or attributes. Initialize by passing a string to the constructor. 

370 

371 Access the raw (null-escaped) text with ``str(<instance>)`` 

372 and unescaped text with ``<instance>.astext()``. 

373 """ 

374 

375 tagname = '#text' 

376 

377 children = () 

378 """Text nodes have no children, and cannot have children.""" 

379 

380 def __new__(cls, data, rawsource=None): 

381 """Assert that `data` is not an array of bytes 

382 and warn if the deprecated `rawsource` argument is used. 

383 """ 

384 if isinstance(data, bytes): 

385 raise TypeError('expecting str data, not bytes') 

386 if rawsource is not None: 

387 warnings.warn('nodes.Text: initialization argument "rawsource" ' 

388 'is ignored and will be removed in Docutils 2.0.', 

389 DeprecationWarning, stacklevel=2) 

390 return str.__new__(cls, data) 

391 

392 def shortrepr(self, maxlen=18): 

393 data = self 

394 if len(data) > maxlen: 

395 data = data[:maxlen-4] + ' ...' 

396 return '<%s: %r>' % (self.tagname, str(data)) 

397 

398 def __repr__(self): 

399 return self.shortrepr(maxlen=68) 

400 

401 def _dom_node(self, domroot): 

402 return domroot.createTextNode(str(self)) 

403 

404 def astext(self): 

405 return str(unescape(self)) 

406 

407 def copy(self): 

408 return self.__class__(str(self)) 

409 

410 def deepcopy(self): 

411 return self.copy() 

412 

413 def pformat(self, indent=' ', level=0): 

414 try: 

415 if self.document.settings.detailed: 

416 tag = '%s%s' % (indent*level, '<#text>') 

417 lines = (indent*(level+1) + repr(line) 

418 for line in self.splitlines(True)) 

419 return '\n'.join((tag, *lines)) + '\n' 

420 except AttributeError: 

421 pass 

422 indent = indent * level 

423 lines = [indent+line for line in self.astext().splitlines()] 

424 if not lines: 

425 return '' 

426 return '\n'.join(lines) + '\n' 

427 

428 # rstrip and lstrip are used by substitution definitions where 

429 # they are expected to return a Text instance, this was formerly 

430 # taken care of by UserString. 

431 

432 def rstrip(self, chars=None): 

433 return self.__class__(str.rstrip(self, chars)) 

434 

435 def lstrip(self, chars=None): 

436 return self.__class__(str.lstrip(self, chars)) 

437 

438 

439class Element(Node): 

440 

441 """ 

442 `Element` is the superclass to all specific elements. 

443 

444 Elements contain attributes and child nodes. 

445 They can be described as a cross between a list and a dictionary. 

446 

447 Elements emulate dictionaries for external [#]_ attributes, indexing by 

448 attribute name (a string). To set the attribute 'att' to 'value', do:: 

449 

450 element['att'] = 'value' 

451 

452 .. [#] External attributes correspond to the XML element attributes. 

453 From its `Node` superclass, Element also inherits "internal" 

454 class attributes that are accessed using the standard syntax, e.g. 

455 ``element.parent``. 

456 

457 There are two special attributes: 'ids' and 'names'. Both are 

458 lists of unique identifiers: 'ids' conform to the regular expression 

459 ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function for rationale and 

460 details). 'names' serve as user-friendly interfaces to IDs; they are 

461 case- and whitespace-normalized (see the fully_normalize_name() function). 

462 

463 Elements emulate lists for child nodes (element nodes and/or text 

464 nodes), indexing by integer. To get the first child node, use:: 

465 

466 element[0] 

467 

468 to iterate over the child nodes (without descending), use:: 

469 

470 for child in element: 

471 ... 

472 

473 Elements may be constructed using the ``+=`` operator. To add one new 

474 child node to element, do:: 

475 

476 element += node 

477 

478 This is equivalent to ``element.append(node)``. 

479 

480 To add a list of multiple child nodes at once, use the same ``+=`` 

481 operator:: 

482 

483 element += [node1, node2] 

484 

485 This is equivalent to ``element.extend([node1, node2])``. 

486 """ 

487 

488 basic_attributes = ('ids', 'classes', 'names', 'dupnames') 

489 """Tuple of attributes which are defined for every Element-derived class 

490 instance and can be safely transferred to a different node.""" 

491 

492 local_attributes = ('backrefs',) 

493 """Tuple of class-specific attributes that should not be copied with the 

494 standard attributes when replacing a node. 

495 

496 NOTE: Derived classes should override this value to prevent any of its 

497 attributes being copied by adding to the value in its parent class.""" 

498 

499 list_attributes = basic_attributes + local_attributes 

500 """Tuple of attributes that are automatically initialized to empty lists 

501 for all nodes.""" 

502 

503 known_attributes = list_attributes + ('source',) 

504 """Tuple of attributes that are known to the Element base class.""" 

505 

506 tagname = None 

507 """The element generic identifier. If None, it is set as an instance 

508 attribute to the name of the class.""" 

509 

510 child_text_separator = '\n\n' 

511 """Separator for child nodes, used by `astext()` method.""" 

512 

513 def __init__(self, rawsource='', *children, **attributes): 

514 self.rawsource = rawsource 

515 """The raw text from which this element was constructed. 

516 

517 NOTE: some elements do not set this value (default ''). 

518 """ 

519 

520 self.children = [] 

521 """List of child nodes (elements and/or `Text`).""" 

522 

523 self.extend(children) # maintain parent info 

524 

525 self.attributes = {} 

526 """Dictionary of attribute {name: value}.""" 

527 

528 # Initialize list attributes. 

529 for att in self.list_attributes: 

530 self.attributes[att] = [] 

531 

532 for att, value in attributes.items(): 

533 att = att.lower() 

534 if att in self.list_attributes: 

535 # mutable list; make a copy for this node 

536 self.attributes[att] = value[:] 

537 else: 

538 self.attributes[att] = value 

539 

540 if self.tagname is None: 

541 self.tagname = self.__class__.__name__ 

542 

543 def _dom_node(self, domroot): 

544 element = domroot.createElement(self.tagname) 

545 for attribute, value in self.attlist(): 

546 if isinstance(value, list): 

547 value = ' '.join(serial_escape('%s' % (v,)) for v in value) 

548 element.setAttribute(attribute, '%s' % value) 

549 for child in self.children: 

550 element.appendChild(child._dom_node(domroot)) 

551 return element 

552 

553 def __repr__(self): 

554 data = '' 

555 for c in self.children: 

556 data += c.shortrepr() 

557 if len(data) > 60: 

558 data = data[:56] + ' ...' 

559 break 

560 if self['names']: 

561 return '<%s "%s": %s>' % (self.__class__.__name__, 

562 '; '.join(self['names']), data) 

563 else: 

564 return '<%s: %s>' % (self.__class__.__name__, data) 

565 

566 def shortrepr(self): 

567 if self['names']: 

568 return '<%s "%s"...>' % (self.__class__.__name__, 

569 '; '.join(self['names'])) 

570 else: 

571 return '<%s...>' % self.tagname 

572 

573 def __str__(self): 

574 if self.children: 

575 return '%s%s%s' % (self.starttag(), 

576 ''.join(str(c) for c in self.children), 

577 self.endtag()) 

578 else: 

579 return self.emptytag() 

580 

581 def starttag(self, quoteattr=None): 

582 # the optional arg is used by the docutils_xml writer 

583 if quoteattr is None: 

584 quoteattr = pseudo_quoteattr 

585 parts = [self.tagname] 

586 for name, value in self.attlist(): 

587 if value is None: # boolean attribute 

588 parts.append('%s="True"' % name) 

589 continue 

590 if isinstance(value, list): 

591 values = [serial_escape('%s' % (v,)) for v in value] 

592 value = ' '.join(values) 

593 else: 

594 value = str(value) 

595 value = quoteattr(value) 

596 parts.append('%s=%s' % (name, value)) 

597 return '<%s>' % ' '.join(parts) 

598 

599 def endtag(self): 

600 return '</%s>' % self.tagname 

601 

602 def emptytag(self): 

603 attributes = ('%s="%s"' % (n, v) for n, v in self.attlist()) 

604 return '<%s/>' % ' '.join((self.tagname, *attributes)) 

605 

606 def __len__(self): 

607 return len(self.children) 

608 

609 def __contains__(self, key): 

610 # Test for both, children and attributes with operator ``in``. 

611 if isinstance(key, str): 

612 return key in self.attributes 

613 return key in self.children 

614 

615 def __getitem__(self, key): 

616 if isinstance(key, str): 

617 return self.attributes[key] 

618 elif isinstance(key, int): 

619 return self.children[key] 

620 elif isinstance(key, slice): 

621 assert key.step in (None, 1), 'cannot handle slice with stride' 

622 return self.children[key.start:key.stop] 

623 else: 

624 raise TypeError('element index must be an integer, a slice, or ' 

625 'an attribute name string') 

626 

627 def __setitem__(self, key, item): 

628 if isinstance(key, str): 

629 self.attributes[str(key)] = item 

630 elif isinstance(key, int): 

631 self.setup_child(item) 

632 self.children[key] = item 

633 elif isinstance(key, slice): 

634 assert key.step in (None, 1), 'cannot handle slice with stride' 

635 for node in item: 

636 self.setup_child(node) 

637 self.children[key.start:key.stop] = item 

638 else: 

639 raise TypeError('element index must be an integer, a slice, or ' 

640 'an attribute name string') 

641 

642 def __delitem__(self, key): 

643 if isinstance(key, str): 

644 del self.attributes[key] 

645 elif isinstance(key, int): 

646 del self.children[key] 

647 elif isinstance(key, slice): 

648 assert key.step in (None, 1), 'cannot handle slice with stride' 

649 del self.children[key.start:key.stop] 

650 else: 

651 raise TypeError('element index must be an integer, a simple ' 

652 'slice, or an attribute name string') 

653 

654 def __add__(self, other): 

655 return self.children + other 

656 

657 def __radd__(self, other): 

658 return other + self.children 

659 

660 def __iadd__(self, other): 

661 """Append a node or a list of nodes to `self.children`.""" 

662 if isinstance(other, Node): 

663 self.append(other) 

664 elif other is not None: 

665 self.extend(other) 

666 return self 

667 

668 def astext(self): 

669 return self.child_text_separator.join( 

670 [child.astext() for child in self.children]) 

671 

672 def non_default_attributes(self): 

673 atts = {} 

674 for key, value in self.attributes.items(): 

675 if self.is_not_default(key): 

676 atts[key] = value 

677 return atts 

678 

679 def attlist(self): 

680 return sorted(self.non_default_attributes().items()) 

681 

682 def get(self, key, failobj=None): 

683 return self.attributes.get(key, failobj) 

684 

685 def hasattr(self, attr): 

686 return attr in self.attributes 

687 

688 def delattr(self, attr): 

689 if attr in self.attributes: 

690 del self.attributes[attr] 

691 

692 def setdefault(self, key, failobj=None): 

693 return self.attributes.setdefault(key, failobj) 

694 

695 has_key = hasattr 

696 

697 def get_language_code(self, fallback=''): 

698 """Return node's language tag. 

699 

700 Look iteratively in self and parents for a class argument 

701 starting with ``language-`` and return the remainder of it 

702 (which should be a `BCP49` language tag) or the `fallback`. 

703 """ 

704 for cls in self.get('classes', []): 

705 if cls.startswith('language-'): 

706 return cls[9:] 

707 try: 

708 return self.parent.get_language(fallback) 

709 except AttributeError: 

710 return fallback 

711 

712 def append(self, item): 

713 self.setup_child(item) 

714 self.children.append(item) 

715 

716 def extend(self, item): 

717 for node in item: 

718 self.append(node) 

719 

720 def insert(self, index, item): 

721 if isinstance(item, Node): 

722 self.setup_child(item) 

723 self.children.insert(index, item) 

724 elif item is not None: 

725 self[index:index] = item 

726 

727 def pop(self, i=-1): 

728 return self.children.pop(i) 

729 

730 def remove(self, item): 

731 self.children.remove(item) 

732 

733 def index(self, item, start=0, stop=sys.maxsize): 

734 return self.children.index(item, start, stop) 

735 

736 def previous_sibling(self): 

737 """Return preceding sibling node or ``None``.""" 

738 try: 

739 i = self.parent.index(self) 

740 except (AttributeError): 

741 return None 

742 return self.parent[i-1] if i > 0 else None 

743 

744 def is_not_default(self, key): 

745 if self[key] == [] and key in self.list_attributes: 

746 return 0 

747 else: 

748 return 1 

749 

750 def update_basic_atts(self, dict_): 

751 """ 

752 Update basic attributes ('ids', 'names', 'classes', 

753 'dupnames', but not 'source') from node or dictionary `dict_`. 

754 """ 

755 if isinstance(dict_, Node): 

756 dict_ = dict_.attributes 

757 for att in self.basic_attributes: 

758 self.append_attr_list(att, dict_.get(att, [])) 

759 

760 def append_attr_list(self, attr, values): 

761 """ 

762 For each element in values, if it does not exist in self[attr], append 

763 it. 

764 

765 NOTE: Requires self[attr] and values to be sequence type and the 

766 former should specifically be a list. 

767 """ 

768 # List Concatenation 

769 for value in values: 

770 if value not in self[attr]: 

771 self[attr].append(value) 

772 

773 def coerce_append_attr_list(self, attr, value): 

774 """ 

775 First, convert both self[attr] and value to a non-string sequence 

776 type; if either is not already a sequence, convert it to a list of one 

777 element. Then call append_attr_list. 

778 

779 NOTE: self[attr] and value both must not be None. 

780 """ 

781 # List Concatenation 

782 if not isinstance(self.get(attr), list): 

783 self[attr] = [self[attr]] 

784 if not isinstance(value, list): 

785 value = [value] 

786 self.append_attr_list(attr, value) 

787 

788 def replace_attr(self, attr, value, force=True): 

789 """ 

790 If self[attr] does not exist or force is True or omitted, set 

791 self[attr] to value, otherwise do nothing. 

792 """ 

793 # One or the other 

794 if force or self.get(attr) is None: 

795 self[attr] = value 

796 

797 def copy_attr_convert(self, attr, value, replace=True): 

798 """ 

799 If attr is an attribute of self, set self[attr] to 

800 [self[attr], value], otherwise set self[attr] to value. 

801 

802 NOTE: replace is not used by this function and is kept only for 

803 compatibility with the other copy functions. 

804 """ 

805 if self.get(attr) is not value: 

806 self.coerce_append_attr_list(attr, value) 

807 

808 def copy_attr_coerce(self, attr, value, replace): 

809 """ 

810 If attr is an attribute of self and either self[attr] or value is a 

811 list, convert all non-sequence values to a sequence of 1 element and 

812 then concatenate the two sequence, setting the result to self[attr]. 

813 If both self[attr] and value are non-sequences and replace is True or 

814 self[attr] is None, replace self[attr] with value. Otherwise, do 

815 nothing. 

816 """ 

817 if self.get(attr) is not value: 

818 if isinstance(self.get(attr), list) or \ 

819 isinstance(value, list): 

820 self.coerce_append_attr_list(attr, value) 

821 else: 

822 self.replace_attr(attr, value, replace) 

823 

824 def copy_attr_concatenate(self, attr, value, replace): 

825 """ 

826 If attr is an attribute of self and both self[attr] and value are 

827 lists, concatenate the two sequences, setting the result to 

828 self[attr]. If either self[attr] or value are non-sequences and 

829 replace is True or self[attr] is None, replace self[attr] with value. 

830 Otherwise, do nothing. 

831 """ 

832 if self.get(attr) is not value: 

833 if isinstance(self.get(attr), list) and \ 

834 isinstance(value, list): 

835 self.append_attr_list(attr, value) 

836 else: 

837 self.replace_attr(attr, value, replace) 

838 

839 def copy_attr_consistent(self, attr, value, replace): 

840 """ 

841 If replace is True or self[attr] is None, replace self[attr] with 

842 value. Otherwise, do nothing. 

843 """ 

844 if self.get(attr) is not value: 

845 self.replace_attr(attr, value, replace) 

846 

847 def update_all_atts(self, dict_, update_fun=copy_attr_consistent, 

848 replace=True, and_source=False): 

849 """ 

850 Updates all attributes from node or dictionary `dict_`. 

851 

852 Appends the basic attributes ('ids', 'names', 'classes', 

853 'dupnames', but not 'source') and then, for all other attributes in 

854 dict_, updates the same attribute in self. When attributes with the 

855 same identifier appear in both self and dict_, the two values are 

856 merged based on the value of update_fun. Generally, when replace is 

857 True, the values in self are replaced or merged with the values in 

858 dict_; otherwise, the values in self may be preserved or merged. When 

859 and_source is True, the 'source' attribute is included in the copy. 

860 

861 NOTE: When replace is False, and self contains a 'source' attribute, 

862 'source' is not replaced even when dict_ has a 'source' 

863 attribute, though it may still be merged into a list depending 

864 on the value of update_fun. 

865 NOTE: It is easier to call the update-specific methods then to pass 

866 the update_fun method to this function. 

867 """ 

868 if isinstance(dict_, Node): 

869 dict_ = dict_.attributes 

870 

871 # Include the source attribute when copying? 

872 if and_source: 

873 filter_fun = self.is_not_list_attribute 

874 else: 

875 filter_fun = self.is_not_known_attribute 

876 

877 # Copy the basic attributes 

878 self.update_basic_atts(dict_) 

879 

880 # Grab other attributes in dict_ not in self except the 

881 # (All basic attributes should be copied already) 

882 for att in filter(filter_fun, dict_): 

883 update_fun(self, att, dict_[att], replace) 

884 

885 def update_all_atts_consistantly(self, dict_, replace=True, 

886 and_source=False): 

887 """ 

888 Updates all attributes from node or dictionary `dict_`. 

889 

890 Appends the basic attributes ('ids', 'names', 'classes', 

891 'dupnames', but not 'source') and then, for all other attributes in 

892 dict_, updates the same attribute in self. When attributes with the 

893 same identifier appear in both self and dict_ and replace is True, the 

894 values in self are replaced with the values in dict_; otherwise, the 

895 values in self are preserved. When and_source is True, the 'source' 

896 attribute is included in the copy. 

897 

898 NOTE: When replace is False, and self contains a 'source' attribute, 

899 'source' is not replaced even when dict_ has a 'source' 

900 attribute, though it may still be merged into a list depending 

901 on the value of update_fun. 

902 """ 

903 self.update_all_atts(dict_, Element.copy_attr_consistent, replace, 

904 and_source) 

905 

906 def update_all_atts_concatenating(self, dict_, replace=True, 

907 and_source=False): 

908 """ 

909 Updates all attributes from node or dictionary `dict_`. 

910 

911 Appends the basic attributes ('ids', 'names', 'classes', 

912 'dupnames', but not 'source') and then, for all other attributes in 

913 dict_, updates the same attribute in self. When attributes with the 

914 same identifier appear in both self and dict_ whose values aren't each 

915 lists and replace is True, the values in self are replaced with the 

916 values in dict_; if the values from self and dict_ for the given 

917 identifier are both of list type, then the two lists are concatenated 

918 and the result stored in self; otherwise, the values in self are 

919 preserved. When and_source is True, the 'source' attribute is 

920 included in the copy. 

921 

922 NOTE: When replace is False, and self contains a 'source' attribute, 

923 'source' is not replaced even when dict_ has a 'source' 

924 attribute, though it may still be merged into a list depending 

925 on the value of update_fun. 

926 """ 

927 self.update_all_atts(dict_, Element.copy_attr_concatenate, replace, 

928 and_source) 

929 

930 def update_all_atts_coercion(self, dict_, replace=True, 

931 and_source=False): 

932 """ 

933 Updates all attributes from node or dictionary `dict_`. 

934 

935 Appends the basic attributes ('ids', 'names', 'classes', 

936 'dupnames', but not 'source') and then, for all other attributes in 

937 dict_, updates the same attribute in self. When attributes with the 

938 same identifier appear in both self and dict_ whose values are both 

939 not lists and replace is True, the values in self are replaced with 

940 the values in dict_; if either of the values from self and dict_ for 

941 the given identifier are of list type, then first any non-lists are 

942 converted to 1-element lists and then the two lists are concatenated 

943 and the result stored in self; otherwise, the values in self are 

944 preserved. When and_source is True, the 'source' attribute is 

945 included in the copy. 

946 

947 NOTE: When replace is False, and self contains a 'source' attribute, 

948 'source' is not replaced even when dict_ has a 'source' 

949 attribute, though it may still be merged into a list depending 

950 on the value of update_fun. 

951 """ 

952 self.update_all_atts(dict_, Element.copy_attr_coerce, replace, 

953 and_source) 

954 

955 def update_all_atts_convert(self, dict_, and_source=False): 

956 """ 

957 Updates all attributes from node or dictionary `dict_`. 

958 

959 Appends the basic attributes ('ids', 'names', 'classes', 

960 'dupnames', but not 'source') and then, for all other attributes in 

961 dict_, updates the same attribute in self. When attributes with the 

962 same identifier appear in both self and dict_ then first any non-lists 

963 are converted to 1-element lists and then the two lists are 

964 concatenated and the result stored in self; otherwise, the values in 

965 self are preserved. When and_source is True, the 'source' attribute 

966 is included in the copy. 

967 

968 NOTE: When replace is False, and self contains a 'source' attribute, 

969 'source' is not replaced even when dict_ has a 'source' 

970 attribute, though it may still be merged into a list depending 

971 on the value of update_fun. 

972 """ 

973 self.update_all_atts(dict_, Element.copy_attr_convert, 

974 and_source=and_source) 

975 

976 def clear(self): 

977 self.children = [] 

978 

979 def replace(self, old, new): 

980 """Replace one child `Node` with another child or children.""" 

981 index = self.index(old) 

982 if isinstance(new, Node): 

983 self.setup_child(new) 

984 self[index] = new 

985 elif new is not None: 

986 self[index:index+1] = new 

987 

988 def replace_self(self, new): 

989 """ 

990 Replace `self` node with `new`, where `new` is a node or a 

991 list of nodes. 

992 """ 

993 update = new 

994 if not isinstance(new, Node): 

995 # `new` is a list; update first child. 

996 try: 

997 update = new[0] 

998 except IndexError: 

999 update = None 

1000 if isinstance(update, Element): 

1001 update.update_basic_atts(self) 

1002 else: 

1003 # `update` is a Text node or `new` is an empty list. 

1004 # Assert that we aren't losing any attributes. 

1005 for att in self.basic_attributes: 

1006 assert not self[att], \ 

1007 'Losing "%s" attribute: %s' % (att, self[att]) 

1008 self.parent.replace(self, new) 

1009 

1010 def first_child_matching_class(self, childclass, start=0, end=sys.maxsize): 

1011 """ 

1012 Return the index of the first child whose class exactly matches. 

1013 

1014 Parameters: 

1015 

1016 - `childclass`: A `Node` subclass to search for, or a tuple of `Node` 

1017 classes. If a tuple, any of the classes may match. 

1018 - `start`: Initial index to check. 

1019 - `end`: Initial index to *not* check. 

1020 """ 

1021 if not isinstance(childclass, tuple): 

1022 childclass = (childclass,) 

1023 for index in range(start, min(len(self), end)): 

1024 for c in childclass: 

1025 if isinstance(self[index], c): 

1026 return index 

1027 return None 

1028 

1029 def first_child_not_matching_class(self, childclass, start=0, 

1030 end=sys.maxsize): 

1031 """ 

1032 Return the index of the first child whose class does *not* match. 

1033 

1034 Parameters: 

1035 

1036 - `childclass`: A `Node` subclass to skip, or a tuple of `Node` 

1037 classes. If a tuple, none of the classes may match. 

1038 - `start`: Initial index to check. 

1039 - `end`: Initial index to *not* check. 

1040 """ 

1041 if not isinstance(childclass, tuple): 

1042 childclass = (childclass,) 

1043 for index in range(start, min(len(self), end)): 

1044 for c in childclass: 

1045 if isinstance(self.children[index], c): 

1046 break 

1047 else: 

1048 return index 

1049 return None 

1050 

1051 def pformat(self, indent=' ', level=0): 

1052 tagline = '%s%s\n' % (indent*level, self.starttag()) 

1053 childreps = (c.pformat(indent, level+1) for c in self.children) 

1054 return ''.join((tagline, *childreps)) 

1055 

1056 def copy(self): 

1057 obj = self.__class__(rawsource=self.rawsource, **self.attributes) 

1058 obj._document = self._document 

1059 obj.source = self.source 

1060 obj.line = self.line 

1061 return obj 

1062 

1063 def deepcopy(self): 

1064 copy = self.copy() 

1065 copy.extend([child.deepcopy() for child in self.children]) 

1066 return copy 

1067 

1068 def set_class(self, name): 

1069 """Add a new class to the "classes" attribute.""" 

1070 warnings.warn('docutils.nodes.Element.set_class() is deprecated; ' 

1071 ' and will be removed in Docutils 0.21 or later.' 

1072 "Append to Element['classes'] list attribute directly", 

1073 DeprecationWarning, stacklevel=2) 

1074 assert ' ' not in name 

1075 self['classes'].append(name.lower()) 

1076 

1077 def note_referenced_by(self, name=None, id=None): 

1078 """Note that this Element has been referenced by its name 

1079 `name` or id `id`.""" 

1080 self.referenced = 1 

1081 # Element.expect_referenced_by_* dictionaries map names or ids 

1082 # to nodes whose ``referenced`` attribute is set to true as 

1083 # soon as this node is referenced by the given name or id. 

1084 # Needed for target propagation. 

1085 by_name = getattr(self, 'expect_referenced_by_name', {}).get(name) 

1086 by_id = getattr(self, 'expect_referenced_by_id', {}).get(id) 

1087 if by_name: 

1088 assert name is not None 

1089 by_name.referenced = 1 

1090 if by_id: 

1091 assert id is not None 

1092 by_id.referenced = 1 

1093 

1094 @classmethod 

1095 def is_not_list_attribute(cls, attr): 

1096 """ 

1097 Returns True if and only if the given attribute is NOT one of the 

1098 basic list attributes defined for all Elements. 

1099 """ 

1100 return attr not in cls.list_attributes 

1101 

1102 @classmethod 

1103 def is_not_known_attribute(cls, attr): 

1104 """ 

1105 Returns True if and only if the given attribute is NOT recognized by 

1106 this class. 

1107 """ 

1108 return attr not in cls.known_attributes 

1109 

1110 

1111class TextElement(Element): 

1112 

1113 """ 

1114 An element which directly contains text. 

1115 

1116 Its children are all `Text` or `Inline` subclass nodes. You can 

1117 check whether an element's context is inline simply by checking whether 

1118 its immediate parent is a `TextElement` instance (including subclasses). 

1119 This is handy for nodes like `image` that can appear both inline and as 

1120 standalone body elements. 

1121 

1122 If passing children to `__init__()`, make sure to set `text` to 

1123 ``''`` or some other suitable value. 

1124 """ 

1125 

1126 child_text_separator = '' 

1127 """Separator for child nodes, used by `astext()` method.""" 

1128 

1129 def __init__(self, rawsource='', text='', *children, **attributes): 

1130 if text != '': 

1131 textnode = Text(text) 

1132 Element.__init__(self, rawsource, textnode, *children, 

1133 **attributes) 

1134 else: 

1135 Element.__init__(self, rawsource, *children, **attributes) 

1136 

1137 

1138class FixedTextElement(TextElement): 

1139 

1140 """An element which directly contains preformatted text.""" 

1141 

1142 def __init__(self, rawsource='', text='', *children, **attributes): 

1143 TextElement.__init__(self, rawsource, text, *children, **attributes) 

1144 self.attributes['xml:space'] = 'preserve' 

1145 

1146 

1147# ======== 

1148# Mixins 

1149# ======== 

1150 

1151class Resolvable: 

1152 

1153 resolved = 0 

1154 

1155 

1156class BackLinkable: 

1157 

1158 def add_backref(self, refid): 

1159 self['backrefs'].append(refid) 

1160 

1161 

1162# ==================== 

1163# Element Categories 

1164# ==================== 

1165 

1166class Root: 

1167 pass 

1168 

1169 

1170class Titular: 

1171 pass 

1172 

1173 

1174class PreBibliographic: 

1175 """Category of Node which may occur before Bibliographic Nodes.""" 

1176 

1177 

1178class Bibliographic: 

1179 pass 

1180 

1181 

1182class Decorative(PreBibliographic): 

1183 pass 

1184 

1185 

1186class Structural: 

1187 pass 

1188 

1189 

1190class Body: 

1191 pass 

1192 

1193 

1194class General(Body): 

1195 pass 

1196 

1197 

1198class Sequential(Body): 

1199 """List-like elements.""" 

1200 

1201 

1202class Admonition(Body): pass 

1203 

1204 

1205class Special(Body): 

1206 """Special internal body elements.""" 

1207 

1208 

1209class Invisible(PreBibliographic): 

1210 """Internal elements that don't appear in output.""" 

1211 

1212 

1213class Part: 

1214 pass 

1215 

1216 

1217class Inline: 

1218 pass 

1219 

1220 

1221class Referential(Resolvable): 

1222 pass 

1223 

1224 

1225class Targetable(Resolvable): 

1226 

1227 referenced = 0 

1228 

1229 indirect_reference_name = None 

1230 """Holds the whitespace_normalized_name (contains mixed case) of a target. 

1231 Required for MoinMoin/reST compatibility.""" 

1232 

1233 

1234class Labeled: 

1235 """Contains a `label` as its first element.""" 

1236 

1237 

1238# ============== 

1239# Root Element 

1240# ============== 

1241 

1242class document(Root, Structural, Element): 

1243 

1244 """ 

1245 The document root element. 

1246 

1247 Do not instantiate this class directly; use 

1248 `docutils.utils.new_document()` instead. 

1249 """ 

1250 

1251 def __init__(self, settings, reporter, *args, **kwargs): 

1252 Element.__init__(self, *args, **kwargs) 

1253 

1254 self.current_source = None 

1255 """Path to or description of the input source being processed.""" 

1256 

1257 self.current_line = None 

1258 """Line number (1-based) of `current_source`.""" 

1259 

1260 self.settings = settings 

1261 """Runtime settings data record.""" 

1262 

1263 self.reporter = reporter 

1264 """System message generator.""" 

1265 

1266 self.indirect_targets = [] 

1267 """List of indirect target nodes.""" 

1268 

1269 self.substitution_defs = {} 

1270 """Mapping of substitution names to substitution_definition nodes.""" 

1271 

1272 self.substitution_names = {} 

1273 """Mapping of case-normalized substitution names to case-sensitive 

1274 names.""" 

1275 

1276 self.refnames = {} 

1277 """Mapping of names to lists of referencing nodes.""" 

1278 

1279 self.refids = {} 

1280 """Mapping of ids to lists of referencing nodes.""" 

1281 

1282 self.nameids = {} 

1283 """Mapping of names to unique id's.""" 

1284 

1285 self.nametypes = {} 

1286 """Mapping of names to hyperlink type (boolean: True => explicit, 

1287 False => implicit.""" 

1288 

1289 self.ids = {} 

1290 """Mapping of ids to nodes.""" 

1291 

1292 self.footnote_refs = {} 

1293 """Mapping of footnote labels to lists of footnote_reference nodes.""" 

1294 

1295 self.citation_refs = {} 

1296 """Mapping of citation labels to lists of citation_reference nodes.""" 

1297 

1298 self.autofootnotes = [] 

1299 """List of auto-numbered footnote nodes.""" 

1300 

1301 self.autofootnote_refs = [] 

1302 """List of auto-numbered footnote_reference nodes.""" 

1303 

1304 self.symbol_footnotes = [] 

1305 """List of symbol footnote nodes.""" 

1306 

1307 self.symbol_footnote_refs = [] 

1308 """List of symbol footnote_reference nodes.""" 

1309 

1310 self.footnotes = [] 

1311 """List of manually-numbered footnote nodes.""" 

1312 

1313 self.citations = [] 

1314 """List of citation nodes.""" 

1315 

1316 self.autofootnote_start = 1 

1317 """Initial auto-numbered footnote number.""" 

1318 

1319 self.symbol_footnote_start = 0 

1320 """Initial symbol footnote symbol index.""" 

1321 

1322 self.id_counter = Counter() 

1323 """Numbers added to otherwise identical IDs.""" 

1324 

1325 self.parse_messages = [] 

1326 """System messages generated while parsing.""" 

1327 

1328 self.transform_messages = [] 

1329 """System messages generated while applying transforms.""" 

1330 

1331 import docutils.transforms 

1332 self.transformer = docutils.transforms.Transformer(self) 

1333 """Storage for transforms to be applied to this document.""" 

1334 

1335 self.include_log = [] 

1336 """The current source's parents (to detect inclusion loops).""" 

1337 

1338 self.decoration = None 

1339 """Document's `decoration` node.""" 

1340 

1341 self._document = self 

1342 

1343 def __getstate__(self): 

1344 """ 

1345 Return dict with unpicklable references removed. 

1346 """ 

1347 state = self.__dict__.copy() 

1348 state['reporter'] = None 

1349 state['transformer'] = None 

1350 return state 

1351 

1352 def asdom(self, dom=None): 

1353 """Return a DOM representation of this document.""" 

1354 if dom is None: 

1355 import xml.dom.minidom as dom 

1356 domroot = dom.Document() 

1357 domroot.appendChild(self._dom_node(domroot)) 

1358 return domroot 

1359 

1360 def set_id(self, node, msgnode=None, suggested_prefix=''): 

1361 if node['ids']: 

1362 # register and check for duplicates 

1363 for id in node['ids']: 

1364 self.ids.setdefault(id, node) 

1365 if self.ids[id] is not node: 

1366 msg = self.reporter.severe('Duplicate ID: "%s".' % id) 

1367 if msgnode is not None: 

1368 msgnode += msg 

1369 return id 

1370 # generate and set id 

1371 id_prefix = self.settings.id_prefix 

1372 auto_id_prefix = self.settings.auto_id_prefix 

1373 base_id = '' 

1374 id = '' 

1375 for name in node['names']: 

1376 if id_prefix: 

1377 # allow names starting with numbers if `id_prefix` 

1378 base_id = make_id('x'+name)[1:] 

1379 else: 

1380 base_id = make_id(name) 

1381 # TODO: normalize id-prefix? (would make code simpler) 

1382 id = id_prefix + base_id 

1383 if base_id and id not in self.ids: 

1384 break 

1385 else: 

1386 if base_id and auto_id_prefix.endswith('%'): 

1387 # disambiguate name-derived ID 

1388 # TODO: remove second condition after announcing change 

1389 prefix = id + '-' 

1390 else: 

1391 prefix = id_prefix + auto_id_prefix 

1392 if prefix.endswith('%'): 

1393 prefix = '%s%s-' % (prefix[:-1], 

1394 suggested_prefix 

1395 or make_id(node.tagname)) 

1396 while True: 

1397 self.id_counter[prefix] += 1 

1398 id = '%s%d' % (prefix, self.id_counter[prefix]) 

1399 if id not in self.ids: 

1400 break 

1401 node['ids'].append(id) 

1402 self.ids[id] = node 

1403 return id 

1404 

1405 def set_name_id_map(self, node, id, msgnode=None, explicit=None): 

1406 """ 

1407 `self.nameids` maps names to IDs, while `self.nametypes` maps names to 

1408 booleans representing hyperlink type (True==explicit, 

1409 False==implicit). This method updates the mappings. 

1410 

1411 The following state transition table shows how `self.nameids` items 

1412 ("id") and `self.nametypes` items ("type") change with new input 

1413 (a call to this method), and what actions are performed 

1414 ("implicit"-type system messages are INFO/1, and 

1415 "explicit"-type system messages are ERROR/3): 

1416 

1417 ==== ===== ======== ======== ======= ==== ===== ===== 

1418 Old State Input Action New State Notes 

1419 ----------- -------- ----------------- ----------- ----- 

1420 id type new type sys.msg. dupname id type 

1421 ==== ===== ======== ======== ======= ==== ===== ===== 

1422 - - explicit - - new True 

1423 - - implicit - - new False 

1424 - False explicit - - new True 

1425 old False explicit implicit old new True 

1426 - True explicit explicit new - True 

1427 old True explicit explicit new,old - True [#]_ 

1428 - False implicit implicit new - False 

1429 old False implicit implicit new,old - False 

1430 - True implicit implicit new - True 

1431 old True implicit implicit new old True 

1432 ==== ===== ======== ======== ======= ==== ===== ===== 

1433 

1434 .. [#] Do not clear the name-to-id map or invalidate the old target if 

1435 both old and new targets are external and refer to identical URIs. 

1436 The new target is invalidated regardless. 

1437 """ 

1438 for name in tuple(node['names']): 

1439 if name in self.nameids: 

1440 self.set_duplicate_name_id(node, id, name, msgnode, explicit) 

1441 # attention: modifies node['names'] 

1442 else: 

1443 self.nameids[name] = id 

1444 self.nametypes[name] = explicit 

1445 

1446 def set_duplicate_name_id(self, node, id, name, msgnode, explicit): 

1447 old_id = self.nameids[name] 

1448 old_explicit = self.nametypes[name] 

1449 self.nametypes[name] = old_explicit or explicit 

1450 if explicit: 

1451 if old_explicit: 

1452 level = 2 

1453 if old_id is not None: 

1454 old_node = self.ids[old_id] 

1455 if 'refuri' in node: 

1456 refuri = node['refuri'] 

1457 if (old_node['names'] 

1458 and 'refuri' in old_node 

1459 and old_node['refuri'] == refuri): 

1460 level = 1 # just inform if refuri's identical 

1461 if level > 1: 

1462 dupname(old_node, name) 

1463 self.nameids[name] = None 

1464 msg = self.reporter.system_message( 

1465 level, 'Duplicate explicit target name: "%s".' % name, 

1466 backrefs=[id], base_node=node) 

1467 if msgnode is not None: 

1468 msgnode += msg 

1469 dupname(node, name) 

1470 else: 

1471 self.nameids[name] = id 

1472 if old_id is not None: 

1473 old_node = self.ids[old_id] 

1474 dupname(old_node, name) 

1475 else: 

1476 if old_id is not None and not old_explicit: 

1477 self.nameids[name] = None 

1478 old_node = self.ids[old_id] 

1479 dupname(old_node, name) 

1480 dupname(node, name) 

1481 if not explicit or (not old_explicit and old_id is not None): 

1482 msg = self.reporter.info( 

1483 'Duplicate implicit target name: "%s".' % name, 

1484 backrefs=[id], base_node=node) 

1485 if msgnode is not None: 

1486 msgnode += msg 

1487 

1488 def has_name(self, name): 

1489 return name in self.nameids 

1490 

1491 # "note" here is an imperative verb: "take note of". 

1492 def note_implicit_target(self, target, msgnode=None): 

1493 id = self.set_id(target, msgnode) 

1494 self.set_name_id_map(target, id, msgnode, explicit=False) 

1495 

1496 def note_explicit_target(self, target, msgnode=None): 

1497 id = self.set_id(target, msgnode) 

1498 self.set_name_id_map(target, id, msgnode, explicit=True) 

1499 

1500 def note_refname(self, node): 

1501 self.refnames.setdefault(node['refname'], []).append(node) 

1502 

1503 def note_refid(self, node): 

1504 self.refids.setdefault(node['refid'], []).append(node) 

1505 

1506 def note_indirect_target(self, target): 

1507 self.indirect_targets.append(target) 

1508 if target['names']: 

1509 self.note_refname(target) 

1510 

1511 def note_anonymous_target(self, target): 

1512 self.set_id(target) 

1513 

1514 def note_autofootnote(self, footnote): 

1515 self.set_id(footnote) 

1516 self.autofootnotes.append(footnote) 

1517 

1518 def note_autofootnote_ref(self, ref): 

1519 self.set_id(ref) 

1520 self.autofootnote_refs.append(ref) 

1521 

1522 def note_symbol_footnote(self, footnote): 

1523 self.set_id(footnote) 

1524 self.symbol_footnotes.append(footnote) 

1525 

1526 def note_symbol_footnote_ref(self, ref): 

1527 self.set_id(ref) 

1528 self.symbol_footnote_refs.append(ref) 

1529 

1530 def note_footnote(self, footnote): 

1531 self.set_id(footnote) 

1532 self.footnotes.append(footnote) 

1533 

1534 def note_footnote_ref(self, ref): 

1535 self.set_id(ref) 

1536 self.footnote_refs.setdefault(ref['refname'], []).append(ref) 

1537 self.note_refname(ref) 

1538 

1539 def note_citation(self, citation): 

1540 self.citations.append(citation) 

1541 

1542 def note_citation_ref(self, ref): 

1543 self.set_id(ref) 

1544 self.citation_refs.setdefault(ref['refname'], []).append(ref) 

1545 self.note_refname(ref) 

1546 

1547 def note_substitution_def(self, subdef, def_name, msgnode=None): 

1548 name = whitespace_normalize_name(def_name) 

1549 if name in self.substitution_defs: 

1550 msg = self.reporter.error( 

1551 'Duplicate substitution definition name: "%s".' % name, 

1552 base_node=subdef) 

1553 if msgnode is not None: 

1554 msgnode += msg 

1555 oldnode = self.substitution_defs[name] 

1556 dupname(oldnode, name) 

1557 # keep only the last definition: 

1558 self.substitution_defs[name] = subdef 

1559 # case-insensitive mapping: 

1560 self.substitution_names[fully_normalize_name(name)] = name 

1561 

1562 def note_substitution_ref(self, subref, refname): 

1563 subref['refname'] = whitespace_normalize_name(refname) 

1564 

1565 def note_pending(self, pending, priority=None): 

1566 self.transformer.add_pending(pending, priority) 

1567 

1568 def note_parse_message(self, message): 

1569 self.parse_messages.append(message) 

1570 

1571 def note_transform_message(self, message): 

1572 self.transform_messages.append(message) 

1573 

1574 def note_source(self, source, offset): 

1575 self.current_source = source 

1576 if offset is None: 

1577 self.current_line = offset 

1578 else: 

1579 self.current_line = offset + 1 

1580 

1581 def copy(self): 

1582 obj = self.__class__(self.settings, self.reporter, 

1583 **self.attributes) 

1584 obj.source = self.source 

1585 obj.line = self.line 

1586 return obj 

1587 

1588 def get_decoration(self): 

1589 if not self.decoration: 

1590 self.decoration = decoration() 

1591 index = self.first_child_not_matching_class((Titular, meta)) 

1592 if index is None: 

1593 self.append(self.decoration) 

1594 else: 

1595 self.insert(index, self.decoration) 

1596 return self.decoration 

1597 

1598 

1599# ================ 

1600# Title Elements 

1601# ================ 

1602 

1603class title(Titular, PreBibliographic, TextElement): pass 

1604class subtitle(Titular, PreBibliographic, TextElement): pass 

1605class rubric(Titular, TextElement): pass 

1606 

1607 

1608# ================== 

1609# Meta-Data Element 

1610# ================== 

1611 

1612class meta(PreBibliographic, Element): 

1613 """Container for "invisible" bibliographic data, or meta-data.""" 

1614 

1615 

1616# ======================== 

1617# Bibliographic Elements 

1618# ======================== 

1619 

1620class docinfo(Bibliographic, Element): pass 

1621class author(Bibliographic, TextElement): pass 

1622class authors(Bibliographic, Element): pass 

1623class organization(Bibliographic, TextElement): pass 

1624class address(Bibliographic, FixedTextElement): pass 

1625class contact(Bibliographic, TextElement): pass 

1626class version(Bibliographic, TextElement): pass 

1627class revision(Bibliographic, TextElement): pass 

1628class status(Bibliographic, TextElement): pass 

1629class date(Bibliographic, TextElement): pass 

1630class copyright(Bibliographic, TextElement): pass 

1631 

1632 

1633# ===================== 

1634# Decorative Elements 

1635# ===================== 

1636 

1637class decoration(Decorative, Element): 

1638 

1639 def get_header(self): 

1640 if not len(self.children) or not isinstance(self.children[0], header): 

1641 self.insert(0, header()) 

1642 return self.children[0] 

1643 

1644 def get_footer(self): 

1645 if not len(self.children) or not isinstance(self.children[-1], footer): 

1646 self.append(footer()) 

1647 return self.children[-1] 

1648 

1649 

1650class header(Decorative, Element): pass 

1651class footer(Decorative, Element): pass 

1652 

1653 

1654# ===================== 

1655# Structural Elements 

1656# ===================== 

1657 

1658class section(Structural, Element): pass 

1659 

1660 

1661class topic(Structural, Element): 

1662 

1663 """ 

1664 Topics are terminal, "leaf" mini-sections, like block quotes with titles, 

1665 or textual figures. A topic is just like a section, except that it has no 

1666 subsections, and it doesn't have to conform to section placement rules. 

1667 

1668 Topics are allowed wherever body elements (list, table, etc.) are allowed, 

1669 but only at the top level of a section or document. Topics cannot nest 

1670 inside topics, sidebars, or body elements; you can't have a topic inside a 

1671 table, list, block quote, etc. 

1672 """ 

1673 

1674 

1675class sidebar(Structural, Element): 

1676 

1677 """ 

1678 Sidebars are like miniature, parallel documents that occur inside other 

1679 documents, providing related or reference material. A sidebar is 

1680 typically offset by a border and "floats" to the side of the page; the 

1681 document's main text may flow around it. Sidebars can also be likened to 

1682 super-footnotes; their content is outside of the flow of the document's 

1683 main text. 

1684 

1685 Sidebars are allowed wherever body elements (list, table, etc.) are 

1686 allowed, but only at the top level of a section or document. Sidebars 

1687 cannot nest inside sidebars, topics, or body elements; you can't have a 

1688 sidebar inside a table, list, block quote, etc. 

1689 """ 

1690 

1691 

1692class transition(Structural, Element): pass 

1693 

1694 

1695# =============== 

1696# Body Elements 

1697# =============== 

1698 

1699class paragraph(General, TextElement): pass 

1700class compound(General, Element): pass 

1701class container(General, Element): pass 

1702class bullet_list(Sequential, Element): pass 

1703class enumerated_list(Sequential, Element): pass 

1704class list_item(Part, Element): pass 

1705class definition_list(Sequential, Element): pass 

1706class definition_list_item(Part, Element): pass 

1707class term(Part, TextElement): pass 

1708class classifier(Part, TextElement): pass 

1709class definition(Part, Element): pass 

1710class field_list(Sequential, Element): pass 

1711class field(Part, Element): pass 

1712class field_name(Part, TextElement): pass 

1713class field_body(Part, Element): pass 

1714 

1715 

1716class option(Part, Element): 

1717 

1718 child_text_separator = '' 

1719 

1720 

1721class option_argument(Part, TextElement): 

1722 

1723 def astext(self): 

1724 return self.get('delimiter', ' ') + TextElement.astext(self) 

1725 

1726 

1727class option_group(Part, Element): 

1728 

1729 child_text_separator = ', ' 

1730 

1731 

1732class option_list(Sequential, Element): pass 

1733 

1734 

1735class option_list_item(Part, Element): 

1736 

1737 child_text_separator = ' ' 

1738 

1739 

1740class option_string(Part, TextElement): pass 

1741class description(Part, Element): pass 

1742class literal_block(General, FixedTextElement): pass 

1743class doctest_block(General, FixedTextElement): pass 

1744class math_block(General, FixedTextElement): pass 

1745class line_block(General, Element): pass 

1746 

1747 

1748class line(Part, TextElement): 

1749 

1750 indent = None 

1751 

1752 

1753class block_quote(General, Element): pass 

1754class attribution(Part, TextElement): pass 

1755class attention(Admonition, Element): pass 

1756class caution(Admonition, Element): pass 

1757class danger(Admonition, Element): pass 

1758class error(Admonition, Element): pass 

1759class important(Admonition, Element): pass 

1760class note(Admonition, Element): pass 

1761class tip(Admonition, Element): pass 

1762class hint(Admonition, Element): pass 

1763class warning(Admonition, Element): pass 

1764class admonition(Admonition, Element): pass 

1765class comment(Special, Invisible, FixedTextElement): pass 

1766class substitution_definition(Special, Invisible, TextElement): pass 

1767class target(Special, Invisible, Inline, TextElement, Targetable): pass 

1768class footnote(General, BackLinkable, Element, Labeled, Targetable): pass 

1769class citation(General, BackLinkable, Element, Labeled, Targetable): pass 

1770class label(Part, TextElement): pass 

1771class figure(General, Element): pass 

1772class caption(Part, TextElement): pass 

1773class legend(Part, Element): pass 

1774class table(General, Element): pass 

1775class tgroup(Part, Element): pass 

1776class colspec(Part, Element): pass 

1777class thead(Part, Element): pass 

1778class tbody(Part, Element): pass 

1779class row(Part, Element): pass 

1780class entry(Part, Element): pass 

1781 

1782 

1783class system_message(Special, BackLinkable, PreBibliographic, Element): 

1784 

1785 """ 

1786 System message element. 

1787 

1788 Do not instantiate this class directly; use 

1789 ``document.reporter.info/warning/error/severe()`` instead. 

1790 """ 

1791 

1792 def __init__(self, message=None, *children, **attributes): 

1793 rawsource = attributes.pop('rawsource', '') 

1794 if message: 

1795 p = paragraph('', message) 

1796 children = (p,) + children 

1797 try: 

1798 Element.__init__(self, rawsource, *children, **attributes) 

1799 except: # noqa catchall 

1800 print('system_message: children=%r' % (children,)) 

1801 raise 

1802 

1803 def astext(self): 

1804 line = self.get('line', '') 

1805 return '%s:%s: (%s/%s) %s' % (self['source'], line, self['type'], 

1806 self['level'], Element.astext(self)) 

1807 

1808 

1809class pending(Special, Invisible, Element): 

1810 

1811 """ 

1812 The "pending" element is used to encapsulate a pending operation: the 

1813 operation (transform), the point at which to apply it, and any data it 

1814 requires. Only the pending operation's location within the document is 

1815 stored in the public document tree (by the "pending" object itself); the 

1816 operation and its data are stored in the "pending" object's internal 

1817 instance attributes. 

1818 

1819 For example, say you want a table of contents in your reStructuredText 

1820 document. The easiest way to specify where to put it is from within the 

1821 document, with a directive:: 

1822 

1823 .. contents:: 

1824 

1825 But the "contents" directive can't do its work until the entire document 

1826 has been parsed and possibly transformed to some extent. So the directive 

1827 code leaves a placeholder behind that will trigger the second phase of its 

1828 processing, something like this:: 

1829 

1830 <pending ...public attributes...> + internal attributes 

1831 

1832 Use `document.note_pending()` so that the 

1833 `docutils.transforms.Transformer` stage of processing can run all pending 

1834 transforms. 

1835 """ 

1836 

1837 def __init__(self, transform, details=None, 

1838 rawsource='', *children, **attributes): 

1839 Element.__init__(self, rawsource, *children, **attributes) 

1840 

1841 self.transform = transform 

1842 """The `docutils.transforms.Transform` class implementing the pending 

1843 operation.""" 

1844 

1845 self.details = details or {} 

1846 """Detail data (dictionary) required by the pending operation.""" 

1847 

1848 def pformat(self, indent=' ', level=0): 

1849 internals = ['.. internal attributes:', 

1850 ' .transform: %s.%s' % (self.transform.__module__, 

1851 self.transform.__name__), 

1852 ' .details:'] 

1853 details = sorted(self.details.items()) 

1854 for key, value in details: 

1855 if isinstance(value, Node): 

1856 internals.append('%7s%s:' % ('', key)) 

1857 internals.extend(['%9s%s' % ('', line) 

1858 for line in value.pformat().splitlines()]) 

1859 elif (value 

1860 and isinstance(value, list) 

1861 and isinstance(value[0], Node)): 

1862 internals.append('%7s%s:' % ('', key)) 

1863 for v in value: 

1864 internals.extend(['%9s%s' % ('', line) 

1865 for line in v.pformat().splitlines()]) 

1866 else: 

1867 internals.append('%7s%s: %r' % ('', key, value)) 

1868 return (Element.pformat(self, indent, level) 

1869 + ''.join((' %s%s\n' % (indent * level, line)) 

1870 for line in internals)) 

1871 

1872 def copy(self): 

1873 obj = self.__class__(self.transform, self.details, self.rawsource, 

1874 **self.attributes) 

1875 obj._document = self._document 

1876 obj.source = self.source 

1877 obj.line = self.line 

1878 return obj 

1879 

1880 

1881class raw(Special, Inline, PreBibliographic, FixedTextElement): 

1882 

1883 """ 

1884 Raw data that is to be passed untouched to the Writer. 

1885 """ 

1886 

1887 

1888# ================= 

1889# Inline Elements 

1890# ================= 

1891 

1892class emphasis(Inline, TextElement): pass 

1893class strong(Inline, TextElement): pass 

1894class literal(Inline, TextElement): pass 

1895class reference(General, Inline, Referential, TextElement): pass 

1896class footnote_reference(Inline, Referential, TextElement): pass 

1897class citation_reference(Inline, Referential, TextElement): pass 

1898class substitution_reference(Inline, TextElement): pass 

1899class title_reference(Inline, TextElement): pass 

1900class abbreviation(Inline, TextElement): pass 

1901class acronym(Inline, TextElement): pass 

1902class superscript(Inline, TextElement): pass 

1903class subscript(Inline, TextElement): pass 

1904class math(Inline, TextElement): pass 

1905 

1906 

1907class image(General, Inline, Element): 

1908 

1909 def astext(self): 

1910 return self.get('alt', '') 

1911 

1912 

1913class inline(Inline, TextElement): pass 

1914class problematic(Inline, TextElement): pass 

1915class generated(Inline, TextElement): pass 

1916 

1917 

1918# ======================================== 

1919# Auxiliary Classes, Functions, and Data 

1920# ======================================== 

1921 

1922node_class_names = """ 

1923 Text 

1924 abbreviation acronym address admonition attention attribution author 

1925 authors 

1926 block_quote bullet_list 

1927 caption caution citation citation_reference classifier colspec comment 

1928 compound contact container copyright 

1929 danger date decoration definition definition_list definition_list_item 

1930 description docinfo doctest_block document 

1931 emphasis entry enumerated_list error 

1932 field field_body field_list field_name figure footer 

1933 footnote footnote_reference 

1934 generated 

1935 header hint 

1936 image important inline 

1937 label legend line line_block list_item literal literal_block 

1938 math math_block meta 

1939 note 

1940 option option_argument option_group option_list option_list_item 

1941 option_string organization 

1942 paragraph pending problematic 

1943 raw reference revision row rubric 

1944 section sidebar status strong subscript substitution_definition 

1945 substitution_reference subtitle superscript system_message 

1946 table target tbody term tgroup thead tip title title_reference topic 

1947 transition 

1948 version 

1949 warning""".split() 

1950"""A list of names of all concrete Node subclasses.""" 

1951 

1952 

1953class NodeVisitor: 

1954 

1955 """ 

1956 "Visitor" pattern [GoF95]_ abstract superclass implementation for 

1957 document tree traversals. 

1958 

1959 Each node class has corresponding methods, doing nothing by 

1960 default; override individual methods for specific and useful 

1961 behaviour. The `dispatch_visit()` method is called by 

1962 `Node.walk()` upon entering a node. `Node.walkabout()` also calls 

1963 the `dispatch_departure()` method before exiting a node. 

1964 

1965 The dispatch methods call "``visit_`` + node class name" or 

1966 "``depart_`` + node class name", resp. 

1967 

1968 This is a base class for visitors whose ``visit_...`` & ``depart_...`` 

1969 methods must be implemented for *all* compulsory node types encountered 

1970 (such as for `docutils.writers.Writer` subclasses). 

1971 Unimplemented methods will raise exceptions (except for optional nodes). 

1972 

1973 For sparse traversals, where only certain node types are of interest, use 

1974 subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform 

1975 processing is desired, subclass `GenericNodeVisitor`. 

1976 

1977 .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of 

1978 Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA, 

1979 1995. 

1980 """ 

1981 

1982 optional = ('meta',) 

1983 """ 

1984 Tuple containing node class names (as strings). 

1985 

1986 No exception will be raised if writers do not implement visit 

1987 or departure functions for these node classes. 

1988 

1989 Used to ensure transitional compatibility with existing 3rd-party writers. 

1990 """ 

1991 

1992 def __init__(self, document): 

1993 self.document = document 

1994 

1995 def dispatch_visit(self, node): 

1996 """ 

1997 Call self."``visit_`` + node class name" with `node` as 

1998 parameter. If the ``visit_...`` method does not exist, call 

1999 self.unknown_visit. 

2000 """ 

2001 node_name = node.__class__.__name__ 

2002 method = getattr(self, 'visit_' + node_name, self.unknown_visit) 

2003 self.document.reporter.debug( 

2004 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s' 

2005 % (method.__name__, node_name)) 

2006 return method(node) 

2007 

2008 def dispatch_departure(self, node): 

2009 """ 

2010 Call self."``depart_`` + node class name" with `node` as 

2011 parameter. If the ``depart_...`` method does not exist, call 

2012 self.unknown_departure. 

2013 """ 

2014 node_name = node.__class__.__name__ 

2015 method = getattr(self, 'depart_' + node_name, self.unknown_departure) 

2016 self.document.reporter.debug( 

2017 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s' 

2018 % (method.__name__, node_name)) 

2019 return method(node) 

2020 

2021 def unknown_visit(self, node): 

2022 """ 

2023 Called when entering unknown `Node` types. 

2024 

2025 Raise an exception unless overridden. 

2026 """ 

2027 if (self.document.settings.strict_visitor 

2028 or node.__class__.__name__ not in self.optional): 

2029 raise NotImplementedError( 

2030 '%s visiting unknown node type: %s' 

2031 % (self.__class__, node.__class__.__name__)) 

2032 

2033 def unknown_departure(self, node): 

2034 """ 

2035 Called before exiting unknown `Node` types. 

2036 

2037 Raise exception unless overridden. 

2038 """ 

2039 if (self.document.settings.strict_visitor 

2040 or node.__class__.__name__ not in self.optional): 

2041 raise NotImplementedError( 

2042 '%s departing unknown node type: %s' 

2043 % (self.__class__, node.__class__.__name__)) 

2044 

2045 

2046class SparseNodeVisitor(NodeVisitor): 

2047 

2048 """ 

2049 Base class for sparse traversals, where only certain node types are of 

2050 interest. When ``visit_...`` & ``depart_...`` methods should be 

2051 implemented for *all* node types (such as for `docutils.writers.Writer` 

2052 subclasses), subclass `NodeVisitor` instead. 

2053 """ 

2054 

2055 

2056class GenericNodeVisitor(NodeVisitor): 

2057 

2058 """ 

2059 Generic "Visitor" abstract superclass, for simple traversals. 

2060 

2061 Unless overridden, each ``visit_...`` method calls `default_visit()`, and 

2062 each ``depart_...`` method (when using `Node.walkabout()`) calls 

2063 `default_departure()`. `default_visit()` (and `default_departure()`) must 

2064 be overridden in subclasses. 

2065 

2066 Define fully generic visitors by overriding `default_visit()` (and 

2067 `default_departure()`) only. Define semi-generic visitors by overriding 

2068 individual ``visit_...()`` (and ``depart_...()``) methods also. 

2069 

2070 `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should 

2071 be overridden for default behavior. 

2072 """ 

2073 

2074 def default_visit(self, node): 

2075 """Override for generic, uniform traversals.""" 

2076 raise NotImplementedError 

2077 

2078 def default_departure(self, node): 

2079 """Override for generic, uniform traversals.""" 

2080 raise NotImplementedError 

2081 

2082 

2083def _call_default_visit(self, node): 

2084 self.default_visit(node) 

2085 

2086 

2087def _call_default_departure(self, node): 

2088 self.default_departure(node) 

2089 

2090 

2091def _nop(self, node): 

2092 pass 

2093 

2094 

2095def _add_node_class_names(names): 

2096 """Save typing with dynamic assignments:""" 

2097 for _name in names: 

2098 setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit) 

2099 setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure) 

2100 setattr(SparseNodeVisitor, 'visit_' + _name, _nop) 

2101 setattr(SparseNodeVisitor, 'depart_' + _name, _nop) 

2102 

2103 

2104_add_node_class_names(node_class_names) 

2105 

2106 

2107class TreeCopyVisitor(GenericNodeVisitor): 

2108 

2109 """ 

2110 Make a complete copy of a tree or branch, including element attributes. 

2111 """ 

2112 

2113 def __init__(self, document): 

2114 GenericNodeVisitor.__init__(self, document) 

2115 self.parent_stack = [] 

2116 self.parent = [] 

2117 

2118 def get_tree_copy(self): 

2119 return self.parent[0] 

2120 

2121 def default_visit(self, node): 

2122 """Copy the current node, and make it the new acting parent.""" 

2123 newnode = node.copy() 

2124 self.parent.append(newnode) 

2125 self.parent_stack.append(self.parent) 

2126 self.parent = newnode 

2127 

2128 def default_departure(self, node): 

2129 """Restore the previous acting parent.""" 

2130 self.parent = self.parent_stack.pop() 

2131 

2132 

2133class TreePruningException(Exception): 

2134 

2135 """ 

2136 Base class for `NodeVisitor`-related tree pruning exceptions. 

2137 

2138 Raise subclasses from within ``visit_...`` or ``depart_...`` methods 

2139 called from `Node.walk()` and `Node.walkabout()` tree traversals to prune 

2140 the tree traversed. 

2141 """ 

2142 

2143 

2144class SkipChildren(TreePruningException): 

2145 

2146 """ 

2147 Do not visit any children of the current node. The current node's 

2148 siblings and ``depart_...`` method are not affected. 

2149 """ 

2150 

2151 

2152class SkipSiblings(TreePruningException): 

2153 

2154 """ 

2155 Do not visit any more siblings (to the right) of the current node. The 

2156 current node's children and its ``depart_...`` method are not affected. 

2157 """ 

2158 

2159 

2160class SkipNode(TreePruningException): 

2161 

2162 """ 

2163 Do not visit the current node's children, and do not call the current 

2164 node's ``depart_...`` method. 

2165 """ 

2166 

2167 

2168class SkipDeparture(TreePruningException): 

2169 

2170 """ 

2171 Do not call the current node's ``depart_...`` method. The current node's 

2172 children and siblings are not affected. 

2173 """ 

2174 

2175 

2176class NodeFound(TreePruningException): 

2177 

2178 """ 

2179 Raise to indicate that the target of a search has been found. This 

2180 exception must be caught by the client; it is not caught by the traversal 

2181 code. 

2182 """ 

2183 

2184 

2185class StopTraversal(TreePruningException): 

2186 

2187 """ 

2188 Stop the traversal altogether. The current node's ``depart_...`` method 

2189 is not affected. The parent nodes ``depart_...`` methods are also called 

2190 as usual. No other nodes are visited. This is an alternative to 

2191 NodeFound that does not cause exception handling to trickle up to the 

2192 caller. 

2193 """ 

2194 

2195 

2196def make_id(string): 

2197 """ 

2198 Convert `string` into an identifier and return it. 

2199 

2200 Docutils identifiers will conform to the regular expression 

2201 ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class" 

2202 and "id" attributes) should have no underscores, colons, or periods. 

2203 Hyphens may be used. 

2204 

2205 - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens: 

2206 

2207 ID and NAME tokens must begin with a letter ([A-Za-z]) and may be 

2208 followed by any number of letters, digits ([0-9]), hyphens ("-"), 

2209 underscores ("_"), colons (":"), and periods ("."). 

2210 

2211 - However the `CSS1 spec`_ defines identifiers based on the "name" token, 

2212 a tighter interpretation ("flex" tokenizer notation; "latin1" and 

2213 "escape" 8-bit characters have been replaced with entities):: 

2214 

2215 unicode \\[0-9a-f]{1,4} 

2216 latin1 [&iexcl;-&yuml;] 

2217 escape {unicode}|\\[ -~&iexcl;-&yuml;] 

2218 nmchar [-a-z0-9]|{latin1}|{escape} 

2219 name {nmchar}+ 

2220 

2221 The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"), 

2222 or periods ("."), therefore "class" and "id" attributes should not contain 

2223 these characters. They should be replaced with hyphens ("-"). Combined 

2224 with HTML's requirements (the first character must be a letter; no 

2225 "unicode", "latin1", or "escape" characters), this results in the 

2226 ``[a-z](-?[a-z0-9]+)*`` pattern. 

2227 

2228 .. _HTML 4.01 spec: https://www.w3.org/TR/html401 

2229 .. _CSS1 spec: https://www.w3.org/TR/REC-CSS1 

2230 """ 

2231 id = string.lower() 

2232 id = id.translate(_non_id_translate_digraphs) 

2233 id = id.translate(_non_id_translate) 

2234 # get rid of non-ascii characters. 

2235 # 'ascii' lowercase to prevent problems with turkish locale. 

2236 id = unicodedata.normalize( 

2237 'NFKD', id).encode('ascii', 'ignore').decode('ascii') 

2238 # shrink runs of whitespace and replace by hyphen 

2239 id = _non_id_chars.sub('-', ' '.join(id.split())) 

2240 id = _non_id_at_ends.sub('', id) 

2241 return str(id) 

2242 

2243 

2244_non_id_chars = re.compile('[^a-z0-9]+') 

2245_non_id_at_ends = re.compile('^[-0-9]+|-+$') 

2246_non_id_translate = { 

2247 0x00f8: 'o', # o with stroke 

2248 0x0111: 'd', # d with stroke 

2249 0x0127: 'h', # h with stroke 

2250 0x0131: 'i', # dotless i 

2251 0x0142: 'l', # l with stroke 

2252 0x0167: 't', # t with stroke 

2253 0x0180: 'b', # b with stroke 

2254 0x0183: 'b', # b with topbar 

2255 0x0188: 'c', # c with hook 

2256 0x018c: 'd', # d with topbar 

2257 0x0192: 'f', # f with hook 

2258 0x0199: 'k', # k with hook 

2259 0x019a: 'l', # l with bar 

2260 0x019e: 'n', # n with long right leg 

2261 0x01a5: 'p', # p with hook 

2262 0x01ab: 't', # t with palatal hook 

2263 0x01ad: 't', # t with hook 

2264 0x01b4: 'y', # y with hook 

2265 0x01b6: 'z', # z with stroke 

2266 0x01e5: 'g', # g with stroke 

2267 0x0225: 'z', # z with hook 

2268 0x0234: 'l', # l with curl 

2269 0x0235: 'n', # n with curl 

2270 0x0236: 't', # t with curl 

2271 0x0237: 'j', # dotless j 

2272 0x023c: 'c', # c with stroke 

2273 0x023f: 's', # s with swash tail 

2274 0x0240: 'z', # z with swash tail 

2275 0x0247: 'e', # e with stroke 

2276 0x0249: 'j', # j with stroke 

2277 0x024b: 'q', # q with hook tail 

2278 0x024d: 'r', # r with stroke 

2279 0x024f: 'y', # y with stroke 

2280} 

2281_non_id_translate_digraphs = { 

2282 0x00df: 'sz', # ligature sz 

2283 0x00e6: 'ae', # ae 

2284 0x0153: 'oe', # ligature oe 

2285 0x0238: 'db', # db digraph 

2286 0x0239: 'qp', # qp digraph 

2287} 

2288 

2289 

2290def dupname(node, name): 

2291 node['dupnames'].append(name) 

2292 node['names'].remove(name) 

2293 # Assume that this method is referenced, even though it isn't; we 

2294 # don't want to throw unnecessary system_messages. 

2295 node.referenced = 1 

2296 

2297 

2298def fully_normalize_name(name): 

2299 """Return a case- and whitespace-normalized name.""" 

2300 return ' '.join(name.lower().split()) 

2301 

2302 

2303def whitespace_normalize_name(name): 

2304 """Return a whitespace-normalized name.""" 

2305 return ' '.join(name.split()) 

2306 

2307 

2308def serial_escape(value): 

2309 """Escape string values that are elements of a list, for serialization.""" 

2310 return value.replace('\\', r'\\').replace(' ', r'\ ') 

2311 

2312 

2313def pseudo_quoteattr(value): 

2314 """Quote attributes for pseudo-xml""" 

2315 return '"%s"' % value