Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/docutils/transforms/__init__.py: 89%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

72 statements  

1# $Id: __init__.py 10357 2026-06-14 21:16:29Z milde $ 

2# Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer 

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

4 

5""" 

6This package contains modules for standard tree transforms available 

7to Docutils components. Tree transforms serve a variety of purposes: 

8 

9- To tie up certain syntax-specific "loose ends" that remain after the 

10 initial parsing of the input plaintext. These transforms are used to 

11 supplement a limited syntax. 

12 

13- To automate the internal linking of the document tree (hyperlink 

14 references, footnote references, etc.). 

15 

16- To extract useful information from the document tree. These 

17 transforms may be used to construct (for example) indexes and tables 

18 of contents. 

19 

20Each transform is an optional step that a Docutils component may 

21choose to perform on the parsed document. 

22""" 

23 

24from __future__ import annotations 

25 

26__docformat__ = 'reStructuredText' 

27 

28from docutils import languages, ApplicationError, TransformSpec 

29 

30 

31class TransformError(ApplicationError): 

32 pass 

33 

34 

35class Transform: 

36 """Docutils transform component abstract base class.""" 

37 

38 default_priority = None 

39 """Numerical priority of this transform, 0 through 999 (override).""" 

40 

41 def __init__(self, document, startnode=None) -> None: 

42 """ 

43 Initial setup for in-place document transforms. 

44 """ 

45 

46 self.document = document 

47 """The document tree to transform.""" 

48 

49 self.startnode = startnode 

50 """Node from which to begin the transform. For many transforms which 

51 apply to the document as a whole, `startnode` is not set (i.e. its 

52 value is `None`).""" 

53 

54 self.language = languages.get_language( 

55 document.settings.language_code, document.reporter) 

56 """Language module local to this document.""" 

57 

58 def apply(self, **kwargs): 

59 """Override to apply the transform to the document tree.""" 

60 raise NotImplementedError('subclass must override this method') 

61 

62 

63class Transformer(TransformSpec): 

64 """ 

65 Store "transforms" and apply them to the document tree. 

66 

67 Collect lists of `Transform` instances from Docutils 

68 components (`TransformSpec` instances). 

69 Apply collected "transforms" to the document tree. 

70 

71 Also keeps track of components by component type name. 

72 

73 https://docutils.sourceforge.io/docs/peps/pep-0258.html#transformer 

74 """ 

75 

76 def __init__(self, document) -> None: 

77 self.transforms = [] 

78 """List of transforms to apply. Each item is a 4-tuple: 

79 ``(priority string, transform class, pending node or None, kwargs)``. 

80 """ 

81 

82 self.document = document 

83 """The `nodes.document` object this Transformer is attached to.""" 

84 

85 self.applied = [] 

86 """Transforms already applied, in order.""" 

87 

88 self.sorted = False 

89 """Boolean: is `self.tranforms` sorted?""" 

90 

91 self.components = {} 

92 """Mapping of component type name to component object. 

93 

94 Set by `self.populate_from_components()`. 

95 """ 

96 

97 self.serialno = 0 

98 """Internal serial number to keep track of the add order of 

99 transforms.""" 

100 

101 def add_transform(self, transform_class, priority=None, **kwargs) -> None: 

102 """ 

103 Store a single transform. Use `priority` to override the default. 

104 `kwargs` is a dictionary whose contents are passed as keyword 

105 arguments to the `apply` method of the transform. This can be used to 

106 pass application-specific data to the transform instance. 

107 """ 

108 if priority is None: 

109 priority = transform_class.default_priority 

110 priority_string = self.get_priority_string(priority) 

111 self.transforms.append( 

112 (priority_string, transform_class, None, kwargs)) 

113 self.sorted = False 

114 

115 def add_transforms(self, transform_list) -> None: 

116 """Store multiple transforms, with default priorities.""" 

117 for transform_class in transform_list: 

118 priority_string = self.get_priority_string( 

119 transform_class.default_priority) 

120 self.transforms.append( 

121 (priority_string, transform_class, None, {})) 

122 self.sorted = False 

123 

124 def add_pending(self, pending, priority=None) -> None: 

125 """Store a transform with an associated `pending` node.""" 

126 transform_class = pending.transform 

127 if priority is None: 

128 priority = transform_class.default_priority 

129 priority_string = self.get_priority_string(priority) 

130 self.transforms.append( 

131 (priority_string, transform_class, pending, {})) 

132 self.sorted = False 

133 

134 def get_priority_string(self, priority) -> str: 

135 """ 

136 Return a string, `priority` combined with `self.serialno`. 

137 

138 This ensures FIFO order on transforms with identical priority. 

139 """ 

140 self.serialno += 1 

141 return '%03d-%03d' % (priority, self.serialno) 

142 

143 def populate_from_components(self, components) -> None: 

144 """ 

145 Store each component's default transforms. 

146 

147 Transforms are stored with default priorities for later sorting. 

148 Components that don't inherit from `TransformSpec` are ignored. 

149 

150 Also, store components by type name in a mapping for later lookup. 

151 """ 

152 for component in components: 

153 if not isinstance(component, TransformSpec): 

154 continue 

155 self.add_transforms(component.get_transforms()) 

156 self.components[component.component_type] = component 

157 self.sorted = False # sort transform list in self.apply_transforms() 

158 

159 def apply_transforms(self) -> None: 

160 """Apply all of the stored transforms, in priority order.""" 

161 self.document.reporter.attach_observer( 

162 self.document.note_transform_message) 

163 while self.transforms: 

164 if not self.sorted: 

165 # Unsorted initially, and whenever a transform is added 

166 # (transforms may add other transforms). 

167 self.transforms.sort(reverse=True) 

168 self.sorted = True 

169 priority, transform_class, pending, kwargs = self.transforms.pop() 

170 transform = transform_class(self.document, startnode=pending) 

171 transform.apply(**kwargs) 

172 self.applied.append((priority, transform_class, pending, kwargs)) 

173 self.document.reporter.detach_observer( 

174 self.document.note_transform_message)