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

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

83 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 

24from __future__ import annotations 

25 

26__docformat__ = 'reStructuredText' 

27 

28import warnings 

29 

30from docutils import languages, ApplicationError, TransformSpec 

31 

32 

33class TransformError(ApplicationError): 

34 pass 

35 

36 

37class Transform: 

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

39 

40 default_priority = None 

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

42 

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

44 """ 

45 Initial setup for in-place document transforms. 

46 """ 

47 

48 self.document = document 

49 """The document tree to transform.""" 

50 

51 self.startnode = startnode 

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

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

54 value is `None`).""" 

55 

56 self.language = languages.get_language( 

57 document.settings.language_code, document.reporter) 

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

59 

60 def apply(self, **kwargs): 

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

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

63 

64 

65class Transformer(TransformSpec): 

66 """ 

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

68 

69 Collect lists of `Transform` instances from Docutils 

70 components (`TransformSpec` instances). 

71 Apply collected "transforms" to the document tree. 

72 

73 Also keeps track of components by component type name. 

74 

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

76 """ 

77 

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

79 self.transforms = [] 

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

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

82 """ 

83 

84 self.unknown_reference_resolvers = [] 

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

86 

87 Deprecated. Will be removed in Docutils 1.0. 

88 """ 

89 

90 self.document = document 

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

92 

93 self.applied = [] 

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

95 

96 self.sorted = False 

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

98 

99 self.components = {} 

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

101 

102 Set by `self.populate_from_components()`. 

103 """ 

104 

105 self.serialno = 0 

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

107 transforms.""" 

108 

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

110 """ 

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

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

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

114 pass application-specific data to the transform instance. 

115 """ 

116 if priority is None: 

117 priority = transform_class.default_priority 

118 priority_string = self.get_priority_string(priority) 

119 self.transforms.append( 

120 (priority_string, transform_class, None, kwargs)) 

121 self.sorted = False 

122 

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

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

125 for transform_class in transform_list: 

126 priority_string = self.get_priority_string( 

127 transform_class.default_priority) 

128 self.transforms.append( 

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

130 self.sorted = False 

131 

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

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

134 transform_class = pending.transform 

135 if priority is None: 

136 priority = transform_class.default_priority 

137 priority_string = self.get_priority_string(priority) 

138 self.transforms.append( 

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

140 self.sorted = False 

141 

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

143 """ 

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

145 

146 This ensures FIFO order on transforms with identical priority. 

147 """ 

148 self.serialno += 1 

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

150 

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

152 """ 

153 Store each component's default transforms and reference resolvers. 

154 

155 Transforms are stored with default priorities for later sorting. 

156 "Unknown reference resolvers" are sorted and stored. 

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

158 

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

160 """ 

161 resolvers = [] 

162 for component in components: 

163 if not isinstance(component, TransformSpec): 

164 continue 

165 self.add_transforms(component.get_transforms()) 

166 self.components[component.component_type] = component 

167 resolvers.extend(component.unknown_reference_resolvers) 

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

169 

170 # Sort and add hook functions helping to resolve unknown references. 

171 def keyfun(f): 

172 return f.priority 

173 resolvers.sort(key=keyfun) 

174 self.unknown_reference_resolvers += resolvers 

175 if self.unknown_reference_resolvers: 

176 warnings.warn('The `unknown_reference_resolvers` hook chain ' 

177 'will be removed in Docutils 1.0.\n' 

178 'Use a transform to resolve references.', 

179 DeprecationWarning, stacklevel=2) 

180 

181 def apply_transforms(self) -> None: 

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

183 self.document.reporter.attach_observer( 

184 self.document.note_transform_message) 

185 while self.transforms: 

186 if not self.sorted: 

187 # Unsorted initially, and whenever a transform is added 

188 # (transforms may add other transforms). 

189 self.transforms.sort(reverse=True) 

190 self.sorted = True 

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

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

193 transform.apply(**kwargs) 

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

195 self.document.reporter.detach_observer( 

196 self.document.note_transform_message)