Coverage for coverage / plugin_support.py: 53.191%

174 statements  

« prev     ^ index     » next       coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000

1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt 

3 

4"""Support for plugins.""" 

5 

6from __future__ import annotations 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

7 

8import os 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

9import os.path 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

10import sys 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

11from collections.abc import Iterable, Iterator 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

12from types import FrameType 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

13from typing import Any, Callable 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

14 

15from coverage.exceptions import PluginError 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

16from coverage.misc import isolate_module 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

17from coverage.plugin import CoveragePlugin, FileReporter, FileTracer 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

18from coverage.types import TArc, TConfigurable, TDebugCtl, TLineNo, TPluginConfig, TSourceTokenLines 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

19 

20os = isolate_module(os) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

21 

22 

23class Plugins: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

24 """The currently loaded collection of coverage.py plugins.""" 

25 

26 def __init__(self, debug: TDebugCtl | None = None) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

27 self.order: list[CoveragePlugin] = [] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

28 self.names: dict[str, CoveragePlugin] = {} 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

29 self.file_tracers: list[CoveragePlugin] = [] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

30 self.configurers: list[CoveragePlugin] = [] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

31 self.context_switchers: list[CoveragePlugin] = [] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

32 

33 self.current_module: str | None = None 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

34 self.debug = debug 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

35 

36 def load_from_config( 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

37 self, 

38 modules: Iterable[str], 

39 config: TPluginConfig, 

40 ) -> None: 

41 """Load plugin modules, and read their settings from configuration.""" 

42 

43 for module in modules: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

44 self.current_module = module 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

45 __import__(module) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

46 mod = sys.modules[module] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

47 

48 coverage_init = getattr(mod, "coverage_init", None) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

49 if not coverage_init: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

50 raise PluginError( 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0A1234

51 f"Plugin module {module!r} didn't define a coverage_init function", 

52 ) 

53 

54 options = config.get_plugin_options(module) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

55 coverage_init(self, options) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

56 

57 self.current_module = None 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

58 

59 def load_from_callables( 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

60 self, 

61 plugin_inits: Iterable[TCoverageInit], 

62 ) -> None: 

63 """Load plugins from callables provided.""" 

64 for fn in plugin_inits: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

65 fn(self) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

66 

67 def add_file_tracer(self, plugin: CoveragePlugin) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

68 """Add a file tracer plugin. 

69 

70 `plugin` is an instance of a third-party plugin class. It must 

71 implement the :meth:`CoveragePlugin.file_tracer` method. 

72 

73 """ 

74 self._add_plugin(plugin, self.file_tracers) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

75 

76 def add_configurer(self, plugin: CoveragePlugin) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

77 """Add a configuring plugin. 

78 

79 `plugin` is an instance of a third-party plugin class. It must 

80 implement the :meth:`CoveragePlugin.configure` method. 

81 

82 """ 

83 self._add_plugin(plugin, self.configurers) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

84 

85 def add_dynamic_context(self, plugin: CoveragePlugin) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

86 """Add a dynamic context plugin. 

87 

88 `plugin` is an instance of a third-party plugin class. It must 

89 implement the :meth:`CoveragePlugin.dynamic_context` method. 

90 

91 """ 

92 self._add_plugin(plugin, self.context_switchers) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0A1234

93 

94 def add_noop(self, plugin: CoveragePlugin) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

95 """Add a plugin that does nothing. 

96 

97 This is only useful for testing the plugin support. 

98 

99 """ 

100 self._add_plugin(plugin, None) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

101 

102 def _add_plugin( 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

103 self, 

104 plugin: CoveragePlugin, 

105 specialized: list[CoveragePlugin] | None, 

106 ) -> None: 

107 """Add a plugin object. 

108 

109 `plugin` is a :class:`CoveragePlugin` instance to add. `specialized` 

110 is a list to append the plugin to. 

111 

112 """ 

113 plugin_name = f"{self.current_module}.{plugin.__class__.__name__}" 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

114 if self.debug and self.debug.should("plugin"): 114 ↛ 115line 114 didn't jump to line 115 because the condition on line 114 was never true1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

115 self.debug.write(f"Loaded plugin {self.current_module!r}: {plugin!r}") 

116 labelled = LabelledDebug(f"plugin {self.current_module!r}", self.debug) 

117 plugin = DebugPluginWrapper(plugin, labelled) 

118 

119 plugin._coverage_plugin_name = plugin_name 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

120 plugin._coverage_enabled = True 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

121 self.order.append(plugin) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

122 self.names[plugin_name] = plugin 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

123 if specialized is not None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

124 specialized.append(plugin) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

125 

126 def __bool__(self) -> bool: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

127 return bool(self.order) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

128 

129 def __iter__(self) -> Iterator[CoveragePlugin]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

130 return iter(self.order) 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

131 

132 def get(self, plugin_name: str) -> CoveragePlugin: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

133 """Return a plugin by name.""" 

134 return self.names[plugin_name] 1abcdefghijklmnopqrstuvwxyzA

135 

136 

137TCoverageInit = Callable[[Plugins], None] 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

138 

139 

140class LabelledDebug: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

141 """A Debug writer, but with labels for prepending to the messages.""" 

142 

143 def __init__(self, label: str, debug: TDebugCtl, prev_labels: Iterable[str] = ()): 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

144 self.labels = list(prev_labels) + [label] 

145 self.debug = debug 

146 

147 def add_label(self, label: str) -> LabelledDebug: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

148 """Add a label to the writer, and return a new `LabelledDebug`.""" 

149 return LabelledDebug(label, self.debug, self.labels) 

150 

151 def message_prefix(self) -> str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

152 """The prefix to use on messages, combining the labels.""" 

153 prefixes = self.labels + [""] 

154 return ":\n".join(" " * i + label for i, label in enumerate(prefixes)) 

155 

156 def write(self, message: str) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

157 """Write `message`, but with the labels prepended.""" 

158 self.debug.write(f"{self.message_prefix()}{message}") 

159 

160 

161class DebugPluginWrapper(CoveragePlugin): 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

162 """Wrap a plugin, and use debug to report on what it's doing.""" 

163 

164 def __init__(self, plugin: CoveragePlugin, debug: LabelledDebug) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

165 super().__init__() 

166 self.plugin = plugin 

167 self.debug = debug 

168 

169 def file_tracer(self, filename: str) -> FileTracer | None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

170 tracer = self.plugin.file_tracer(filename) 

171 self.debug.write(f"file_tracer({filename!r}) --> {tracer!r}") 

172 if tracer: 

173 debug = self.debug.add_label(f"file {filename!r}") 

174 tracer = DebugFileTracerWrapper(tracer, debug) 

175 return tracer 

176 

177 def file_reporter(self, filename: str) -> FileReporter | str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

178 reporter = self.plugin.file_reporter(filename) 

179 assert isinstance(reporter, FileReporter) 

180 self.debug.write(f"file_reporter({filename!r}) --> {reporter!r}") 

181 if reporter: 

182 debug = self.debug.add_label(f"file {filename!r}") 

183 reporter = DebugFileReporterWrapper(filename, reporter, debug) 

184 return reporter 

185 

186 def dynamic_context(self, frame: FrameType) -> str | None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

187 context = self.plugin.dynamic_context(frame) 

188 self.debug.write(f"dynamic_context({frame!r}) --> {context!r}") 

189 return context 

190 

191 def find_executable_files(self, src_dir: str) -> Iterable[str]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

192 executable_files = self.plugin.find_executable_files(src_dir) 

193 self.debug.write(f"find_executable_files({src_dir!r}) --> {executable_files!r}") 

194 return executable_files 

195 

196 def configure(self, config: TConfigurable) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

197 self.debug.write(f"configure({config!r})") 

198 self.plugin.configure(config) 

199 

200 def sys_info(self) -> Iterable[tuple[str, Any]]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

201 return self.plugin.sys_info() 

202 

203 

204class DebugFileTracerWrapper(FileTracer): 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

205 """A debugging `FileTracer`.""" 

206 

207 def __init__(self, tracer: FileTracer, debug: LabelledDebug) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

208 self.tracer = tracer 

209 self.debug = debug 

210 

211 def _show_frame(self, frame: FrameType) -> str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

212 """A short string identifying a frame, for debug messages.""" 

213 filename = os.path.basename(frame.f_code.co_filename) 

214 return f"{filename}@{frame.f_lineno}" 

215 

216 def source_filename(self) -> str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

217 sfilename = self.tracer.source_filename() 

218 self.debug.write(f"source_filename() --> {sfilename!r}") 

219 return sfilename 

220 

221 def has_dynamic_source_filename(self) -> bool: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

222 has = self.tracer.has_dynamic_source_filename() 

223 self.debug.write(f"has_dynamic_source_filename() --> {has!r}") 

224 return has 

225 

226 def dynamic_source_filename(self, filename: str, frame: FrameType) -> str | None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

227 dyn = self.tracer.dynamic_source_filename(filename, frame) 

228 self.debug.write( 

229 "dynamic_source_filename({!r}, {}) --> {!r}".format( 

230 filename, 

231 self._show_frame(frame), 

232 dyn, 

233 ) 

234 ) 

235 return dyn 

236 

237 def line_number_range(self, frame: FrameType) -> tuple[TLineNo, TLineNo]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

238 pair = self.tracer.line_number_range(frame) 

239 self.debug.write(f"line_number_range({self._show_frame(frame)}) --> {pair!r}") 

240 return pair 

241 

242 

243class DebugFileReporterWrapper(FileReporter): 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

244 """A debugging `FileReporter`.""" 

245 

246 def __init__(self, filename: str, reporter: FileReporter, debug: LabelledDebug) -> None: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

247 super().__init__(filename) 

248 self.reporter = reporter 

249 self.debug = debug 

250 

251 def relative_filename(self) -> str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

252 ret = self.reporter.relative_filename() 

253 self.debug.write(f"relative_filename() --> {ret!r}") 

254 return ret 

255 

256 def lines(self) -> set[TLineNo]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

257 ret = self.reporter.lines() 

258 self.debug.write(f"lines() --> {ret!r}") 

259 return ret 

260 

261 def excluded_lines(self) -> set[TLineNo]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

262 ret = self.reporter.excluded_lines() 

263 self.debug.write(f"excluded_lines() --> {ret!r}") 

264 return ret 

265 

266 def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

267 ret = self.reporter.translate_lines(lines) 

268 self.debug.write(f"translate_lines({lines!r}) --> {ret!r}") 

269 return ret 

270 

271 def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

272 ret = self.reporter.translate_arcs(arcs) 

273 self.debug.write(f"translate_arcs({arcs!r}) --> {ret!r}") 

274 return ret 

275 

276 def no_branch_lines(self) -> set[TLineNo]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

277 ret = self.reporter.no_branch_lines() 

278 self.debug.write(f"no_branch_lines() --> {ret!r}") 

279 return ret 

280 

281 def exit_counts(self) -> dict[TLineNo, int]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

282 ret = self.reporter.exit_counts() 

283 self.debug.write(f"exit_counts() --> {ret!r}") 

284 return ret 

285 

286 def arcs(self) -> set[TArc]: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

287 ret = self.reporter.arcs() 

288 self.debug.write(f"arcs() --> {ret!r}") 

289 return ret 

290 

291 def source(self) -> str: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

292 ret = self.reporter.source() 

293 self.debug.write(f"source() --> {len(ret)} chars") 

294 return ret 

295 

296 def source_token_lines(self) -> TSourceTokenLines: 1aBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQ5qR6rS7sT8tU9uV!vW#wX$xY%yZ'z0(A1)234

297 ret = list(self.reporter.source_token_lines()) 

298 self.debug.write(f"source_token_lines() --> {len(ret)} tokens") 

299 return ret