Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/docutils/transforms/__init__.py: 36%

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

69 statements  

1# $Id$ 

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 

24__docformat__ = 'reStructuredText' 

25 

26 

27from docutils import languages, ApplicationError, TransformSpec 

28 

29 

30class TransformError(ApplicationError): 

31 pass 

32 

33 

34class Transform: 

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

36 

37 default_priority = None 

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

39 

40 def __init__(self, document, startnode=None): 

41 """ 

42 Initial setup for in-place document transforms. 

43 """ 

44 

45 self.document = document 

46 """The document tree to transform.""" 

47 

48 self.startnode = startnode 

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

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

51 value is `None`).""" 

52 

53 self.language = languages.get_language( 

54 document.settings.language_code, document.reporter) 

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

56 

57 def apply(self, **kwargs): 

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

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

60 

61 

62class Transformer(TransformSpec): 

63 """ 

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

65 

66 Collect lists of `Transform` instances and "unknown_reference_resolvers" 

67 from Docutils components (`TransformSpec` instances). 

68 Apply collected "transforms" to the document tree. 

69 

70 Also keeps track of components by component type name. 

71 

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

73 """ 

74 

75 def __init__(self, document): 

76 self.transforms = [] 

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

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

79 """ 

80 

81 self.unknown_reference_resolvers = [] 

82 """List of hook functions which assist in resolving references.""" 

83 

84 self.document = document 

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

86 

87 self.applied = [] 

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

89 

90 self.sorted = False 

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

92 

93 self.components = {} 

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

95 

96 Set by `self.populate_from_components()`. 

97 """ 

98 

99 self.serialno = 0 

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

101 transforms.""" 

102 

103 def add_transform(self, transform_class, priority=None, **kwargs): 

104 """ 

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

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

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

108 pass application-specific data to the transform instance. 

109 """ 

110 if priority is None: 

111 priority = transform_class.default_priority 

112 priority_string = self.get_priority_string(priority) 

113 self.transforms.append( 

114 (priority_string, transform_class, None, kwargs)) 

115 self.sorted = False 

116 

117 def add_transforms(self, transform_list): 

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

119 for transform_class in transform_list: 

120 priority_string = self.get_priority_string( 

121 transform_class.default_priority) 

122 self.transforms.append( 

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

124 self.sorted = False 

125 

126 def add_pending(self, pending, priority=None): 

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

128 transform_class = pending.transform 

129 if priority is None: 

130 priority = transform_class.default_priority 

131 priority_string = self.get_priority_string(priority) 

132 self.transforms.append( 

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

134 self.sorted = False 

135 

136 def get_priority_string(self, priority): 

137 """ 

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

139 

140 This ensures FIFO order on transforms with identical priority. 

141 """ 

142 self.serialno += 1 

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

144 

145 def populate_from_components(self, components): 

146 """ 

147 Store each component's default transforms and reference resolvers 

148 

149 Transforms are stored with default priorities for later sorting. 

150 "Unknown reference resolvers" are sorted and stored. 

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

152 

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

154 """ 

155 resolvers = [] 

156 for component in components: 

157 if not isinstance(component, TransformSpec): 

158 continue 

159 self.add_transforms(component.get_transforms()) 

160 self.components[component.component_type] = component 

161 resolvers.extend(component.unknown_reference_resolvers) 

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

163 

164 # Sort and add helper functions to help resolve unknown references. 

165 def keyfun(f): 

166 return f.priority 

167 resolvers.sort(key=keyfun) 

168 self.unknown_reference_resolvers += resolvers 

169 

170 def apply_transforms(self): 

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

172 self.document.reporter.attach_observer( 

173 self.document.note_transform_message) 

174 while self.transforms: 

175 if not self.sorted: 

176 # Unsorted initially, and whenever a transform is added 

177 # (transforms may add other transforms). 

178 self.transforms.sort(reverse=True) 

179 self.sorted = True 

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

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

182 transform.apply(**kwargs) 

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

184 self.document.reporter.detach_observer( 

185 self.document.note_transform_message)