Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/docutils/nodes.py: 57%

954 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-14 06:25 +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 `findall()`. 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 

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

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

331 """ 

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

333 Backslash-escaped spaces are also removed. 

334 """ 

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

336 if restore_backslashes: 

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

338 else: 

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

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

341 return text 

342 

343 

344class Text(Node, str): 

345 

346 """ 

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

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

349 

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

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

352 """ 

353 

354 tagname = '#text' 

355 

356 children = () 

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

358 

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

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

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

362 """ 

363 if isinstance(data, bytes): 

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

365 if rawsource is not None: 

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

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

368 DeprecationWarning, stacklevel=2) 

369 return str.__new__(cls, data) 

370 

371 def shortrepr(self, maxlen=18): 

372 data = self 

373 if len(data) > maxlen: 

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

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

376 

377 def __repr__(self): 

378 return self.shortrepr(maxlen=68) 

379 

380 def _dom_node(self, domroot): 

381 return domroot.createTextNode(str(self)) 

382 

383 def astext(self): 

384 return str(unescape(self)) 

385 

386 def copy(self): 

387 return self.__class__(str(self)) 

388 

389 def deepcopy(self): 

390 return self.copy() 

391 

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

393 try: 

394 if self.document.settings.detailed: 

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

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

397 for line in self.splitlines(True)) 

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

399 except AttributeError: 

400 pass 

401 indent = indent * level 

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

403 if not lines: 

404 return '' 

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

406 

407 # rstrip and lstrip are used by substitution definitions where 

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

409 # taken care of by UserString. 

410 

411 def rstrip(self, chars=None): 

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

413 

414 def lstrip(self, chars=None): 

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

416 

417 

418class Element(Node): 

419 

420 """ 

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

422 

423 Elements contain attributes and child nodes. 

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

425 

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

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

428 

429 element['att'] = 'value' 

430 

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

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

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

434 ``element.parent``. 

435 

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

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

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

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

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

441 

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

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

444 

445 element[0] 

446 

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

448 

449 for child in element: 

450 ... 

451 

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

453 child node to element, do:: 

454 

455 element += node 

456 

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

458 

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

460 operator:: 

461 

462 element += [node1, node2] 

463 

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

465 """ 

466 

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

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

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

470 

471 local_attributes = ('backrefs',) 

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

473 standard attributes when replacing a node. 

474 

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

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

477 

478 list_attributes = basic_attributes + local_attributes 

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

480 for all nodes.""" 

481 

482 known_attributes = list_attributes + ('source',) 

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

484 

485 tagname = None 

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

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

488 

489 child_text_separator = '\n\n' 

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

491 

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

493 self.rawsource = rawsource 

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

495 

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

497 """ 

498 

499 self.children = [] 

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

501 

502 self.extend(children) # maintain parent info 

503 

504 self.attributes = {} 

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

506 

507 # Initialize list attributes. 

508 for att in self.list_attributes: 

509 self.attributes[att] = [] 

510 

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

512 att = att.lower() 

513 if att in self.list_attributes: 

514 # mutable list; make a copy for this node 

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

516 else: 

517 self.attributes[att] = value 

518 

519 if self.tagname is None: 

520 self.tagname = self.__class__.__name__ 

521 

522 def _dom_node(self, domroot): 

523 element = domroot.createElement(self.tagname) 

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

525 if isinstance(value, list): 

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

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

528 for child in self.children: 

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

530 return element 

531 

532 def __repr__(self): 

533 data = '' 

534 for c in self.children: 

535 data += c.shortrepr() 

536 if len(data) > 60: 

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

538 break 

539 if self['names']: 

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

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

542 else: 

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

544 

545 def shortrepr(self): 

546 if self['names']: 

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

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

549 else: 

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

551 

552 def __str__(self): 

553 if self.children: 

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

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

556 self.endtag()) 

557 else: 

558 return self.emptytag() 

559 

560 def starttag(self, quoteattr=None): 

561 # the optional arg is used by the docutils_xml writer 

562 if quoteattr is None: 

563 quoteattr = pseudo_quoteattr 

564 parts = [self.tagname] 

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

566 if value is None: # boolean attribute 

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

568 continue 

569 if isinstance(value, list): 

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

571 value = ' '.join(values) 

572 else: 

573 value = str(value) 

574 value = quoteattr(value) 

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

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

577 

578 def endtag(self): 

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

580 

581 def emptytag(self): 

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

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

584 

585 def __len__(self): 

586 return len(self.children) 

587 

588 def __contains__(self, key): 

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

590 if isinstance(key, str): 

591 return key in self.attributes 

592 return key in self.children 

593 

594 def __getitem__(self, key): 

595 if isinstance(key, str): 

596 return self.attributes[key] 

597 elif isinstance(key, int): 

598 return self.children[key] 

599 elif isinstance(key, slice): 

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

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

602 else: 

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

604 'an attribute name string') 

605 

606 def __setitem__(self, key, item): 

607 if isinstance(key, str): 

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

609 elif isinstance(key, int): 

610 self.setup_child(item) 

611 self.children[key] = item 

612 elif isinstance(key, slice): 

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

614 for node in item: 

615 self.setup_child(node) 

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

617 else: 

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

619 'an attribute name string') 

620 

621 def __delitem__(self, key): 

622 if isinstance(key, str): 

623 del self.attributes[key] 

624 elif isinstance(key, int): 

625 del self.children[key] 

626 elif isinstance(key, slice): 

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

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

629 else: 

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

631 'slice, or an attribute name string') 

632 

633 def __add__(self, other): 

634 return self.children + other 

635 

636 def __radd__(self, other): 

637 return other + self.children 

638 

639 def __iadd__(self, other): 

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

641 if isinstance(other, Node): 

642 self.append(other) 

643 elif other is not None: 

644 self.extend(other) 

645 return self 

646 

647 def astext(self): 

648 return self.child_text_separator.join( 

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

650 

651 def non_default_attributes(self): 

652 atts = {} 

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

654 if self.is_not_default(key): 

655 atts[key] = value 

656 return atts 

657 

658 def attlist(self): 

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

660 

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

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

663 

664 def hasattr(self, attr): 

665 return attr in self.attributes 

666 

667 def delattr(self, attr): 

668 if attr in self.attributes: 

669 del self.attributes[attr] 

670 

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

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

673 

674 has_key = hasattr 

675 

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

677 """Return node's language tag. 

678 

679 Look iteratively in self and parents for a class argument 

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

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

682 """ 

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

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

685 return cls[9:] 

686 try: 

687 return self.parent.get_language(fallback) 

688 except AttributeError: 

689 return fallback 

690 

691 def append(self, item): 

692 self.setup_child(item) 

693 self.children.append(item) 

694 

695 def extend(self, item): 

696 for node in item: 

697 self.append(node) 

698 

699 def insert(self, index, item): 

700 if isinstance(item, Node): 

701 self.setup_child(item) 

702 self.children.insert(index, item) 

703 elif item is not None: 

704 self[index:index] = item 

705 

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

707 return self.children.pop(i) 

708 

709 def remove(self, item): 

710 self.children.remove(item) 

711 

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

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

714 

715 def previous_sibling(self): 

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

717 try: 

718 i = self.parent.index(self) 

719 except (AttributeError): 

720 return None 

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

722 

723 def is_not_default(self, key): 

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

725 return 0 

726 else: 

727 return 1 

728 

729 def update_basic_atts(self, dict_): 

730 """ 

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

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

733 """ 

734 if isinstance(dict_, Node): 

735 dict_ = dict_.attributes 

736 for att in self.basic_attributes: 

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

738 

739 def append_attr_list(self, attr, values): 

740 """ 

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

742 it. 

743 

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

745 former should specifically be a list. 

746 """ 

747 # List Concatenation 

748 for value in values: 

749 if value not in self[attr]: 

750 self[attr].append(value) 

751 

752 def coerce_append_attr_list(self, attr, value): 

753 """ 

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

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

756 element. Then call append_attr_list. 

757 

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

759 """ 

760 # List Concatenation 

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

762 self[attr] = [self[attr]] 

763 if not isinstance(value, list): 

764 value = [value] 

765 self.append_attr_list(attr, value) 

766 

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

768 """ 

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

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

771 """ 

772 # One or the other 

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

774 self[attr] = value 

775 

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

777 """ 

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

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

780 

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

782 compatibility with the other copy functions. 

783 """ 

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

785 self.coerce_append_attr_list(attr, value) 

786 

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

788 """ 

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

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

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

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

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

794 nothing. 

795 """ 

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

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

798 isinstance(value, list): 

799 self.coerce_append_attr_list(attr, value) 

800 else: 

801 self.replace_attr(attr, value, replace) 

802 

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

804 """ 

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

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

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

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

809 Otherwise, do nothing. 

810 """ 

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

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

813 isinstance(value, list): 

814 self.append_attr_list(attr, value) 

815 else: 

816 self.replace_attr(attr, value, replace) 

817 

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

819 """ 

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

821 value. Otherwise, do nothing. 

822 """ 

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

824 self.replace_attr(attr, value, replace) 

825 

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

827 replace=True, and_source=False): 

828 """ 

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

830 

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

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

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

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

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

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

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

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

839 

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

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

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

843 on the value of update_fun. 

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

845 the update_fun method to this function. 

846 """ 

847 if isinstance(dict_, Node): 

848 dict_ = dict_.attributes 

849 

850 # Include the source attribute when copying? 

851 if and_source: 

852 filter_fun = self.is_not_list_attribute 

853 else: 

854 filter_fun = self.is_not_known_attribute 

855 

856 # Copy the basic attributes 

857 self.update_basic_atts(dict_) 

858 

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

860 # (All basic attributes should be copied already) 

861 for att in filter(filter_fun, dict_): 

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

863 

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

865 and_source=False): 

866 """ 

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

868 

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

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

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

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

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

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

875 attribute is included in the copy. 

876 

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

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

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

880 on the value of update_fun. 

881 """ 

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

883 and_source) 

884 

885 def update_all_atts_concatenating(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_ whose values aren't each 

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

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

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

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

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

899 included in the copy. 

900 

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

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

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

904 on the value of update_fun. 

905 """ 

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

907 and_source) 

908 

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

910 and_source=False): 

911 """ 

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

913 

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

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

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

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

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

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

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

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

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

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

924 included in the copy. 

925 

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

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

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

929 on the value of update_fun. 

930 """ 

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

932 and_source) 

933 

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

935 """ 

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

937 

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

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

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

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

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

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

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

945 is 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_convert, 

953 and_source=and_source) 

954 

955 def clear(self): 

956 self.children = [] 

957 

958 def replace(self, old, new): 

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

960 index = self.index(old) 

961 if isinstance(new, Node): 

962 self.setup_child(new) 

963 self[index] = new 

964 elif new is not None: 

965 self[index:index+1] = new 

966 

967 def replace_self(self, new): 

968 """ 

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

970 list of nodes. 

971 """ 

972 update = new 

973 if not isinstance(new, Node): 

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

975 try: 

976 update = new[0] 

977 except IndexError: 

978 update = None 

979 if isinstance(update, Element): 

980 update.update_basic_atts(self) 

981 else: 

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

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

984 for att in self.basic_attributes: 

985 assert not self[att], \ 

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

987 self.parent.replace(self, new) 

988 

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

990 """ 

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

992 

993 Parameters: 

994 

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

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

997 - `start`: Initial index to check. 

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

999 """ 

1000 if not isinstance(childclass, tuple): 

1001 childclass = (childclass,) 

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

1003 for c in childclass: 

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

1005 return index 

1006 return None 

1007 

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

1009 end=sys.maxsize): 

1010 """ 

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

1012 

1013 Parameters: 

1014 

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

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

1017 - `start`: Initial index to check. 

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

1019 """ 

1020 if not isinstance(childclass, tuple): 

1021 childclass = (childclass,) 

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

1023 for c in childclass: 

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

1025 break 

1026 else: 

1027 return index 

1028 return None 

1029 

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

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

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

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

1034 

1035 def copy(self): 

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

1037 obj._document = self._document 

1038 obj.source = self.source 

1039 obj.line = self.line 

1040 return obj 

1041 

1042 def deepcopy(self): 

1043 copy = self.copy() 

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

1045 return copy 

1046 

1047 def set_class(self, name): 

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

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

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

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

1052 DeprecationWarning, stacklevel=2) 

1053 assert ' ' not in name 

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

1055 

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

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

1058 `name` or id `id`.""" 

1059 self.referenced = 1 

1060 # Element.expect_referenced_by_* dictionaries map names or ids 

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

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

1063 # Needed for target propagation. 

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

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

1066 if by_name: 

1067 assert name is not None 

1068 by_name.referenced = 1 

1069 if by_id: 

1070 assert id is not None 

1071 by_id.referenced = 1 

1072 

1073 @classmethod 

1074 def is_not_list_attribute(cls, attr): 

1075 """ 

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

1077 basic list attributes defined for all Elements. 

1078 """ 

1079 return attr not in cls.list_attributes 

1080 

1081 @classmethod 

1082 def is_not_known_attribute(cls, attr): 

1083 """ 

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

1085 this class. 

1086 """ 

1087 return attr not in cls.known_attributes 

1088 

1089 

1090class TextElement(Element): 

1091 

1092 """ 

1093 An element which directly contains text. 

1094 

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

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

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

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

1099 standalone body elements. 

1100 

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

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

1103 """ 

1104 

1105 child_text_separator = '' 

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

1107 

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

1109 if text != '': 

1110 textnode = Text(text) 

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

1112 **attributes) 

1113 else: 

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

1115 

1116 

1117class FixedTextElement(TextElement): 

1118 

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

1120 

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

1122 super().__init__(rawsource, text, *children, **attributes) 

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

1124 

1125 

1126# TODO: PureTextElement(TextElement): 

1127# """An element which only contains text, no children.""" 

1128# For elements in the DTD that directly employ #PCDATA in their definition: 

1129# citation_reference, comment, footnote_reference, label, math, math_block, 

1130# option_argument, option_string, raw, 

1131 

1132 

1133# ======== 

1134# Mixins 

1135# ======== 

1136 

1137class Resolvable: 

1138 

1139 resolved = 0 

1140 

1141 

1142class BackLinkable: 

1143 

1144 def add_backref(self, refid): 

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

1146 

1147 

1148# ==================== 

1149# Element Categories 

1150# ==================== 

1151 

1152class Root: 

1153 pass 

1154 

1155 

1156class Titular: 

1157 pass 

1158 

1159 

1160class PreBibliographic: 

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

1162 

1163 

1164class Bibliographic: 

1165 pass 

1166 

1167 

1168class Decorative(PreBibliographic): 

1169 pass 

1170 

1171 

1172class Structural: 

1173 pass 

1174 

1175 

1176class Body: 

1177 pass 

1178 

1179 

1180class General(Body): 

1181 pass 

1182 

1183 

1184class Sequential(Body): 

1185 """List-like elements.""" 

1186 

1187 

1188class Admonition(Body): pass 

1189 

1190 

1191class Special(Body): 

1192 """Special internal body elements.""" 

1193 

1194 

1195class Invisible(PreBibliographic): 

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

1197 

1198 

1199class Part: 

1200 pass 

1201 

1202 

1203class Inline: 

1204 pass 

1205 

1206 

1207class Referential(Resolvable): 

1208 pass 

1209 

1210 

1211class Targetable(Resolvable): 

1212 

1213 referenced = 0 

1214 

1215 indirect_reference_name = None 

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

1217 Required for MoinMoin/reST compatibility.""" 

1218 

1219 

1220class Labeled: 

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

1222 

1223 

1224# ============== 

1225# Root Element 

1226# ============== 

1227 

1228class document(Root, Structural, Element): 

1229 

1230 """ 

1231 The document root element. 

1232 

1233 Do not instantiate this class directly; use 

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

1235 """ 

1236 

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

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

1239 

1240 self.current_source = None 

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

1242 

1243 self.current_line = None 

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

1245 

1246 self.settings = settings 

1247 """Runtime settings data record.""" 

1248 

1249 self.reporter = reporter 

1250 """System message generator.""" 

1251 

1252 self.indirect_targets = [] 

1253 """List of indirect target nodes.""" 

1254 

1255 self.substitution_defs = {} 

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

1257 

1258 self.substitution_names = {} 

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

1260 names.""" 

1261 

1262 self.refnames = {} 

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

1264 

1265 self.refids = {} 

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

1267 

1268 self.nameids = {} 

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

1270 

1271 self.nametypes = {} 

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

1273 False => implicit.""" 

1274 

1275 self.ids = {} 

1276 """Mapping of ids to nodes.""" 

1277 

1278 self.footnote_refs = {} 

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

1280 

1281 self.citation_refs = {} 

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

1283 

1284 self.autofootnotes = [] 

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

1286 

1287 self.autofootnote_refs = [] 

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

1289 

1290 self.symbol_footnotes = [] 

1291 """List of symbol footnote nodes.""" 

1292 

1293 self.symbol_footnote_refs = [] 

1294 """List of symbol footnote_reference nodes.""" 

1295 

1296 self.footnotes = [] 

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

1298 

1299 self.citations = [] 

1300 """List of citation nodes.""" 

1301 

1302 self.autofootnote_start = 1 

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

1304 

1305 self.symbol_footnote_start = 0 

1306 """Initial symbol footnote symbol index.""" 

1307 

1308 self.id_counter = Counter() 

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

1310 

1311 self.parse_messages = [] 

1312 """System messages generated while parsing.""" 

1313 

1314 self.transform_messages = [] 

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

1316 

1317 import docutils.transforms 

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

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

1320 

1321 self.include_log = [] 

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

1323 

1324 self.decoration = None 

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

1326 

1327 self._document = self 

1328 

1329 def __getstate__(self): 

1330 """ 

1331 Return dict with unpicklable references removed. 

1332 """ 

1333 state = self.__dict__.copy() 

1334 state['reporter'] = None 

1335 state['transformer'] = None 

1336 return state 

1337 

1338 def asdom(self, dom=None): 

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

1340 if dom is None: 

1341 import xml.dom.minidom as dom 

1342 domroot = dom.Document() 

1343 domroot.appendChild(self._dom_node(domroot)) 

1344 return domroot 

1345 

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

1347 if node['ids']: 

1348 # register and check for duplicates 

1349 for id in node['ids']: 

1350 self.ids.setdefault(id, node) 

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

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

1353 if msgnode is not None: 

1354 msgnode += msg 

1355 return id 

1356 # generate and set id 

1357 id_prefix = self.settings.id_prefix 

1358 auto_id_prefix = self.settings.auto_id_prefix 

1359 base_id = '' 

1360 id = '' 

1361 for name in node['names']: 

1362 if id_prefix: 

1363 # allow names starting with numbers if `id_prefix` 

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

1365 else: 

1366 base_id = make_id(name) 

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

1368 id = id_prefix + base_id 

1369 if base_id and id not in self.ids: 

1370 break 

1371 else: 

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

1373 # disambiguate name-derived ID 

1374 # TODO: remove second condition after announcing change 

1375 prefix = id + '-' 

1376 else: 

1377 prefix = id_prefix + auto_id_prefix 

1378 if prefix.endswith('%'): 

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

1380 suggested_prefix 

1381 or make_id(node.tagname)) 

1382 while True: 

1383 self.id_counter[prefix] += 1 

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

1385 if id not in self.ids: 

1386 break 

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

1388 self.ids[id] = node 

1389 return id 

1390 

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

1392 """ 

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

1394 booleans representing hyperlink type (True==explicit, 

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

1396 

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

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

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

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

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

1402 

1403 ==== ===== ======== ======== ======= ==== ===== ===== 

1404 Old State Input Action New State Notes 

1405 ----------- -------- ----------------- ----------- ----- 

1406 id type new type sys.msg. dupname id type 

1407 ==== ===== ======== ======== ======= ==== ===== ===== 

1408 - - explicit - - new True 

1409 - - implicit - - new False 

1410 - False explicit - - new True 

1411 old False explicit implicit old new True 

1412 - True explicit explicit new - True 

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

1414 - False implicit implicit new - False 

1415 old False implicit implicit new,old - False 

1416 - True implicit implicit new - True 

1417 old True implicit implicit new old True 

1418 ==== ===== ======== ======== ======= ==== ===== ===== 

1419 

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

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

1422 The new target is invalidated regardless. 

1423 """ 

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

1425 if name in self.nameids: 

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

1427 # attention: modifies node['names'] 

1428 else: 

1429 self.nameids[name] = id 

1430 self.nametypes[name] = explicit 

1431 

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

1433 old_id = self.nameids[name] 

1434 old_explicit = self.nametypes[name] 

1435 self.nametypes[name] = old_explicit or explicit 

1436 if explicit: 

1437 if old_explicit: 

1438 level = 2 

1439 if old_id is not None: 

1440 old_node = self.ids[old_id] 

1441 if 'refuri' in node: 

1442 refuri = node['refuri'] 

1443 if (old_node['names'] 

1444 and 'refuri' in old_node 

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

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

1447 if level > 1: 

1448 dupname(old_node, name) 

1449 self.nameids[name] = None 

1450 msg = self.reporter.system_message( 

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

1452 backrefs=[id], base_node=node) 

1453 if msgnode is not None: 

1454 msgnode += msg 

1455 dupname(node, name) 

1456 else: 

1457 self.nameids[name] = id 

1458 if old_id is not None: 

1459 old_node = self.ids[old_id] 

1460 dupname(old_node, name) 

1461 else: 

1462 if old_id is not None and not old_explicit: 

1463 self.nameids[name] = None 

1464 old_node = self.ids[old_id] 

1465 dupname(old_node, name) 

1466 dupname(node, name) 

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

1468 msg = self.reporter.info( 

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

1470 backrefs=[id], base_node=node) 

1471 if msgnode is not None: 

1472 msgnode += msg 

1473 

1474 def has_name(self, name): 

1475 return name in self.nameids 

1476 

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

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

1479 id = self.set_id(target, msgnode) 

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

1481 

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

1483 id = self.set_id(target, msgnode) 

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

1485 

1486 def note_refname(self, node): 

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

1488 

1489 def note_refid(self, node): 

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

1491 

1492 def note_indirect_target(self, target): 

1493 self.indirect_targets.append(target) 

1494 if target['names']: 

1495 self.note_refname(target) 

1496 

1497 def note_anonymous_target(self, target): 

1498 self.set_id(target) 

1499 

1500 def note_autofootnote(self, footnote): 

1501 self.set_id(footnote) 

1502 self.autofootnotes.append(footnote) 

1503 

1504 def note_autofootnote_ref(self, ref): 

1505 self.set_id(ref) 

1506 self.autofootnote_refs.append(ref) 

1507 

1508 def note_symbol_footnote(self, footnote): 

1509 self.set_id(footnote) 

1510 self.symbol_footnotes.append(footnote) 

1511 

1512 def note_symbol_footnote_ref(self, ref): 

1513 self.set_id(ref) 

1514 self.symbol_footnote_refs.append(ref) 

1515 

1516 def note_footnote(self, footnote): 

1517 self.set_id(footnote) 

1518 self.footnotes.append(footnote) 

1519 

1520 def note_footnote_ref(self, ref): 

1521 self.set_id(ref) 

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

1523 self.note_refname(ref) 

1524 

1525 def note_citation(self, citation): 

1526 self.citations.append(citation) 

1527 

1528 def note_citation_ref(self, ref): 

1529 self.set_id(ref) 

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

1531 self.note_refname(ref) 

1532 

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

1534 name = whitespace_normalize_name(def_name) 

1535 if name in self.substitution_defs: 

1536 msg = self.reporter.error( 

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

1538 base_node=subdef) 

1539 if msgnode is not None: 

1540 msgnode += msg 

1541 oldnode = self.substitution_defs[name] 

1542 dupname(oldnode, name) 

1543 # keep only the last definition: 

1544 self.substitution_defs[name] = subdef 

1545 # case-insensitive mapping: 

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

1547 

1548 def note_substitution_ref(self, subref, refname): 

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

1550 

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

1552 self.transformer.add_pending(pending, priority) 

1553 

1554 def note_parse_message(self, message): 

1555 self.parse_messages.append(message) 

1556 

1557 def note_transform_message(self, message): 

1558 self.transform_messages.append(message) 

1559 

1560 def note_source(self, source, offset): 

1561 self.current_source = source 

1562 if offset is None: 

1563 self.current_line = offset 

1564 else: 

1565 self.current_line = offset + 1 

1566 

1567 def copy(self): 

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

1569 **self.attributes) 

1570 obj.source = self.source 

1571 obj.line = self.line 

1572 return obj 

1573 

1574 def get_decoration(self): 

1575 if not self.decoration: 

1576 self.decoration = decoration() 

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

1578 if index is None: 

1579 self.append(self.decoration) 

1580 else: 

1581 self.insert(index, self.decoration) 

1582 return self.decoration 

1583 

1584 

1585# ================ 

1586# Title Elements 

1587# ================ 

1588 

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

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

1591class rubric(Titular, TextElement): pass 

1592 

1593 

1594# ================== 

1595# Meta-Data Element 

1596# ================== 

1597 

1598class meta(PreBibliographic, Element): 

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

1600 

1601 

1602# ======================== 

1603# Bibliographic Elements 

1604# ======================== 

1605 

1606class docinfo(Bibliographic, Element): pass 

1607class author(Bibliographic, TextElement): pass 

1608class authors(Bibliographic, Element): pass 

1609class organization(Bibliographic, TextElement): pass 

1610class address(Bibliographic, FixedTextElement): pass 

1611class contact(Bibliographic, TextElement): pass 

1612class version(Bibliographic, TextElement): pass 

1613class revision(Bibliographic, TextElement): pass 

1614class status(Bibliographic, TextElement): pass 

1615class date(Bibliographic, TextElement): pass 

1616class copyright(Bibliographic, TextElement): pass 

1617 

1618 

1619# ===================== 

1620# Decorative Elements 

1621# ===================== 

1622 

1623class decoration(Decorative, Element): 

1624 

1625 def get_header(self): 

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

1627 self.insert(0, header()) 

1628 return self.children[0] 

1629 

1630 def get_footer(self): 

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

1632 self.append(footer()) 

1633 return self.children[-1] 

1634 

1635 

1636class header(Decorative, Element): pass 

1637class footer(Decorative, Element): pass 

1638 

1639 

1640# ===================== 

1641# Structural Elements 

1642# ===================== 

1643 

1644class section(Structural, Element): pass 

1645 

1646 

1647class topic(Structural, Element): 

1648 

1649 """ 

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

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

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

1653 

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

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

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

1657 table, list, block quote, etc. 

1658 """ 

1659 

1660 

1661class sidebar(Structural, Element): 

1662 

1663 """ 

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

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

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

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

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

1669 main text. 

1670 

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

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

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

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

1675 """ 

1676 

1677 

1678class transition(Structural, Element): pass 

1679 

1680 

1681# =============== 

1682# Body Elements 

1683# =============== 

1684 

1685class paragraph(General, TextElement): pass 

1686class compound(General, Element): pass 

1687class container(General, Element): pass 

1688class bullet_list(Sequential, Element): pass 

1689class enumerated_list(Sequential, Element): pass 

1690class list_item(Part, Element): pass 

1691class definition_list(Sequential, Element): pass 

1692class definition_list_item(Part, Element): pass 

1693class term(Part, TextElement): pass 

1694class classifier(Part, TextElement): pass 

1695class definition(Part, Element): pass 

1696class field_list(Sequential, Element): pass 

1697class field(Part, Element): pass 

1698class field_name(Part, TextElement): pass 

1699class field_body(Part, Element): pass 

1700 

1701 

1702class option(Part, Element): 

1703 

1704 child_text_separator = '' 

1705 

1706 

1707class option_argument(Part, TextElement): 

1708 

1709 def astext(self): 

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

1711 

1712 

1713class option_group(Part, Element): 

1714 

1715 child_text_separator = ', ' 

1716 

1717 

1718class option_list(Sequential, Element): pass 

1719 

1720 

1721class option_list_item(Part, Element): 

1722 

1723 child_text_separator = ' ' 

1724 

1725 

1726class option_string(Part, TextElement): pass 

1727class description(Part, Element): pass 

1728class literal_block(General, FixedTextElement): pass 

1729class doctest_block(General, FixedTextElement): pass 

1730class math_block(General, FixedTextElement): pass 

1731class line_block(General, Element): pass 

1732 

1733 

1734class line(Part, TextElement): 

1735 

1736 indent = None 

1737 

1738 

1739class block_quote(General, Element): pass 

1740class attribution(Part, TextElement): pass 

1741class attention(Admonition, Element): pass 

1742class caution(Admonition, Element): pass 

1743class danger(Admonition, Element): pass 

1744class error(Admonition, Element): pass 

1745class important(Admonition, Element): pass 

1746class note(Admonition, Element): pass 

1747class tip(Admonition, Element): pass 

1748class hint(Admonition, Element): pass 

1749class warning(Admonition, Element): pass 

1750class admonition(Admonition, Element): pass 

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

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

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

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

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

1756class label(Part, TextElement): pass 

1757class figure(General, Element): pass 

1758class caption(Part, TextElement): pass 

1759class legend(Part, Element): pass 

1760class table(General, Element): pass 

1761class tgroup(Part, Element): pass 

1762class colspec(Part, Element): pass 

1763class thead(Part, Element): pass 

1764class tbody(Part, Element): pass 

1765class row(Part, Element): pass 

1766class entry(Part, Element): pass 

1767 

1768 

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

1770 

1771 """ 

1772 System message element. 

1773 

1774 Do not instantiate this class directly; use 

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

1776 """ 

1777 

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

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

1780 if message: 

1781 p = paragraph('', message) 

1782 children = (p,) + children 

1783 try: 

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

1785 except: # noqa catchall 

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

1787 raise 

1788 

1789 def astext(self): 

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

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

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

1793 

1794 

1795class pending(Special, Invisible, Element): 

1796 

1797 """ 

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

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

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

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

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

1803 instance attributes. 

1804 

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

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

1807 document, with a directive:: 

1808 

1809 .. contents:: 

1810 

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

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

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

1814 processing, something like this:: 

1815 

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

1817 

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

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

1820 transforms. 

1821 """ 

1822 

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

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

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

1826 

1827 self.transform = transform 

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

1829 operation.""" 

1830 

1831 self.details = details or {} 

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

1833 

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

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

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

1837 self.transform.__name__), 

1838 ' .details:'] 

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

1840 for key, value in details: 

1841 if isinstance(value, Node): 

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

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

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

1845 elif (value 

1846 and isinstance(value, list) 

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

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

1849 for v in value: 

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

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

1852 else: 

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

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

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

1856 for line in internals)) 

1857 

1858 def copy(self): 

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

1860 **self.attributes) 

1861 obj._document = self._document 

1862 obj.source = self.source 

1863 obj.line = self.line 

1864 return obj 

1865 

1866 

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

1868 

1869 """ 

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

1871 """ 

1872 

1873 

1874# ================= 

1875# Inline Elements 

1876# ================= 

1877 

1878class emphasis(Inline, TextElement): pass 

1879class strong(Inline, TextElement): pass 

1880class literal(Inline, TextElement): pass 

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

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

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

1884class substitution_reference(Inline, TextElement): pass 

1885class title_reference(Inline, TextElement): pass 

1886class abbreviation(Inline, TextElement): pass 

1887class acronym(Inline, TextElement): pass 

1888class superscript(Inline, TextElement): pass 

1889class subscript(Inline, TextElement): pass 

1890class math(Inline, TextElement): pass 

1891 

1892 

1893class image(General, Inline, Element): 

1894 

1895 def astext(self): 

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

1897 

1898 

1899class inline(Inline, TextElement): pass 

1900class problematic(Inline, TextElement): pass 

1901class generated(Inline, TextElement): pass 

1902 

1903 

1904# ======================================== 

1905# Auxiliary Classes, Functions, and Data 

1906# ======================================== 

1907 

1908node_class_names = """ 

1909 Text 

1910 abbreviation acronym address admonition attention attribution author 

1911 authors 

1912 block_quote bullet_list 

1913 caption caution citation citation_reference classifier colspec comment 

1914 compound contact container copyright 

1915 danger date decoration definition definition_list definition_list_item 

1916 description docinfo doctest_block document 

1917 emphasis entry enumerated_list error 

1918 field field_body field_list field_name figure footer 

1919 footnote footnote_reference 

1920 generated 

1921 header hint 

1922 image important inline 

1923 label legend line line_block list_item literal literal_block 

1924 math math_block meta 

1925 note 

1926 option option_argument option_group option_list option_list_item 

1927 option_string organization 

1928 paragraph pending problematic 

1929 raw reference revision row rubric 

1930 section sidebar status strong subscript substitution_definition 

1931 substitution_reference subtitle superscript system_message 

1932 table target tbody term tgroup thead tip title title_reference topic 

1933 transition 

1934 version 

1935 warning""".split() 

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

1937 

1938 

1939class NodeVisitor: 

1940 

1941 """ 

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

1943 document tree traversals. 

1944 

1945 Each node class has corresponding methods, doing nothing by 

1946 default; override individual methods for specific and useful 

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

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

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

1950 

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

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

1953 

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

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

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

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

1958 

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

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

1961 processing is desired, subclass `GenericNodeVisitor`. 

1962 

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

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

1965 1995. 

1966 """ 

1967 

1968 optional = ('meta',) 

1969 """ 

1970 Tuple containing node class names (as strings). 

1971 

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

1973 or departure functions for these node classes. 

1974 

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

1976 """ 

1977 

1978 def __init__(self, document): 

1979 self.document = document 

1980 

1981 def dispatch_visit(self, node): 

1982 """ 

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

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

1985 self.unknown_visit. 

1986 """ 

1987 node_name = node.__class__.__name__ 

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

1989 self.document.reporter.debug( 

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

1991 % (method.__name__, node_name)) 

1992 return method(node) 

1993 

1994 def dispatch_departure(self, node): 

1995 """ 

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

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

1998 self.unknown_departure. 

1999 """ 

2000 node_name = node.__class__.__name__ 

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

2002 self.document.reporter.debug( 

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

2004 % (method.__name__, node_name)) 

2005 return method(node) 

2006 

2007 def unknown_visit(self, node): 

2008 """ 

2009 Called when entering unknown `Node` types. 

2010 

2011 Raise an exception unless overridden. 

2012 """ 

2013 if (self.document.settings.strict_visitor 

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

2015 raise NotImplementedError( 

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

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

2018 

2019 def unknown_departure(self, node): 

2020 """ 

2021 Called before exiting unknown `Node` types. 

2022 

2023 Raise exception unless overridden. 

2024 """ 

2025 if (self.document.settings.strict_visitor 

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

2027 raise NotImplementedError( 

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

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

2030 

2031 

2032class SparseNodeVisitor(NodeVisitor): 

2033 

2034 """ 

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

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

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

2038 subclasses), subclass `NodeVisitor` instead. 

2039 """ 

2040 

2041 

2042class GenericNodeVisitor(NodeVisitor): 

2043 

2044 """ 

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

2046 

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

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

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

2050 be overridden in subclasses. 

2051 

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

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

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

2055 

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

2057 be overridden for default behavior. 

2058 """ 

2059 

2060 def default_visit(self, node): 

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

2062 raise NotImplementedError 

2063 

2064 def default_departure(self, node): 

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

2066 raise NotImplementedError 

2067 

2068 

2069def _call_default_visit(self, node): 

2070 self.default_visit(node) 

2071 

2072 

2073def _call_default_departure(self, node): 

2074 self.default_departure(node) 

2075 

2076 

2077def _nop(self, node): 

2078 pass 

2079 

2080 

2081def _add_node_class_names(names): 

2082 """Save typing with dynamic assignments:""" 

2083 for _name in names: 

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

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

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

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

2088 

2089 

2090_add_node_class_names(node_class_names) 

2091 

2092 

2093class TreeCopyVisitor(GenericNodeVisitor): 

2094 

2095 """ 

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

2097 """ 

2098 

2099 def __init__(self, document): 

2100 GenericNodeVisitor.__init__(self, document) 

2101 self.parent_stack = [] 

2102 self.parent = [] 

2103 

2104 def get_tree_copy(self): 

2105 return self.parent[0] 

2106 

2107 def default_visit(self, node): 

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

2109 newnode = node.copy() 

2110 self.parent.append(newnode) 

2111 self.parent_stack.append(self.parent) 

2112 self.parent = newnode 

2113 

2114 def default_departure(self, node): 

2115 """Restore the previous acting parent.""" 

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

2117 

2118 

2119class TreePruningException(Exception): 

2120 

2121 """ 

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

2123 

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

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

2126 the tree traversed. 

2127 """ 

2128 

2129 

2130class SkipChildren(TreePruningException): 

2131 

2132 """ 

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

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

2135 """ 

2136 

2137 

2138class SkipSiblings(TreePruningException): 

2139 

2140 """ 

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

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

2143 """ 

2144 

2145 

2146class SkipNode(TreePruningException): 

2147 

2148 """ 

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

2150 node's ``depart_...`` method. 

2151 """ 

2152 

2153 

2154class SkipDeparture(TreePruningException): 

2155 

2156 """ 

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

2158 children and siblings are not affected. 

2159 """ 

2160 

2161 

2162class NodeFound(TreePruningException): 

2163 

2164 """ 

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

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

2167 code. 

2168 """ 

2169 

2170 

2171class StopTraversal(TreePruningException): 

2172 

2173 """ 

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

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

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

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

2178 caller. 

2179 """ 

2180 

2181 

2182def make_id(string): 

2183 """ 

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

2185 

2186 Docutils identifiers will conform to the regular expression 

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

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

2189 Hyphens may be used. 

2190 

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

2192 

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

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

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

2196 

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

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

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

2200 

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

2202 latin1 [&iexcl;-&yuml;] 

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

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

2205 name {nmchar}+ 

2206 

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

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

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

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

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

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

2213 

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

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

2216 """ 

2217 id = string.lower() 

2218 id = id.translate(_non_id_translate_digraphs) 

2219 id = id.translate(_non_id_translate) 

2220 # get rid of non-ascii characters. 

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

2222 id = unicodedata.normalize( 

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

2224 # shrink runs of whitespace and replace by hyphen 

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

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

2227 return str(id) 

2228 

2229 

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

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

2232_non_id_translate = { 

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

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

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

2236 0x0131: 'i', # dotless i 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2257 0x0237: 'j', # dotless j 

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

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

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

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

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

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

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

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

2266} 

2267_non_id_translate_digraphs = { 

2268 0x00df: 'sz', # ligature sz 

2269 0x00e6: 'ae', # ae 

2270 0x0153: 'oe', # ligature oe 

2271 0x0238: 'db', # db digraph 

2272 0x0239: 'qp', # qp digraph 

2273} 

2274 

2275 

2276def dupname(node, name): 

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

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

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

2280 # don't want to throw unnecessary system_messages. 

2281 node.referenced = 1 

2282 

2283 

2284def fully_normalize_name(name): 

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

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

2287 

2288 

2289def whitespace_normalize_name(name): 

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

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

2292 

2293 

2294def serial_escape(value): 

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

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

2297 

2298 

2299def pseudo_quoteattr(value): 

2300 """Quote attributes for pseudo-xml""" 

2301 return '"%s"' % value