eric6/DebugClients/Python/coverage/plugin_support.py

changeset 6942
2602857055c5
parent 6219
d6c795b5ce33
child 7427
362cd1b6f81a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3
4 """Support for plugins."""
5
6 import os
7 import os.path
8 import sys
9
10 from coverage.misc import CoverageException, isolate_module
11 from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
12
13 os = isolate_module(os)
14
15
16 class Plugins(object):
17 """The currently loaded collection of coverage.py plugins."""
18
19 def __init__(self):
20 self.order = []
21 self.names = {}
22 self.file_tracers = []
23 self.configurers = []
24
25 self.current_module = None
26 self.debug = None
27
28 @classmethod
29 def load_plugins(cls, modules, config, debug=None):
30 """Load plugins from `modules`.
31
32 Returns a Plugins object with the loaded and configured plugins.
33
34 """
35 plugins = cls()
36 plugins.debug = debug
37
38 for module in modules:
39 plugins.current_module = module
40 __import__(module)
41 mod = sys.modules[module]
42
43 coverage_init = getattr(mod, "coverage_init", None)
44 if not coverage_init:
45 raise CoverageException(
46 "Plugin module %r didn't define a coverage_init function" % module
47 )
48
49 options = config.get_plugin_options(module)
50 coverage_init(plugins, options)
51
52 plugins.current_module = None
53 return plugins
54
55 def add_file_tracer(self, plugin):
56 """Add a file tracer plugin.
57
58 `plugin` is an instance of a third-party plugin class. It must
59 implement the :meth:`CoveragePlugin.file_tracer` method.
60
61 """
62 self._add_plugin(plugin, self.file_tracers)
63
64 def add_configurer(self, plugin):
65 """Add a configuring plugin.
66
67 `plugin` is an instance of a third-party plugin class. It must
68 implement the :meth:`CoveragePlugin.configure` method.
69
70 """
71 self._add_plugin(plugin, self.configurers)
72
73 def add_noop(self, plugin):
74 """Add a plugin that does nothing.
75
76 This is only useful for testing the plugin support.
77
78 """
79 self._add_plugin(plugin, None)
80
81 def _add_plugin(self, plugin, specialized):
82 """Add a plugin object.
83
84 `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
85 is a list to append the plugin to.
86
87 """
88 plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
89 if self.debug and self.debug.should('plugin'):
90 self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
91 labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
92 plugin = DebugPluginWrapper(plugin, labelled)
93
94 # pylint: disable=attribute-defined-outside-init
95 plugin._coverage_plugin_name = plugin_name
96 plugin._coverage_enabled = True
97 self.order.append(plugin)
98 self.names[plugin_name] = plugin
99 if specialized is not None:
100 specialized.append(plugin)
101
102 def __nonzero__(self):
103 return bool(self.order)
104
105 __bool__ = __nonzero__
106
107 def __iter__(self):
108 return iter(self.order)
109
110 def get(self, plugin_name):
111 """Return a plugin by name."""
112 return self.names[plugin_name]
113
114
115 class LabelledDebug(object):
116 """A Debug writer, but with labels for prepending to the messages."""
117
118 def __init__(self, label, debug, prev_labels=()):
119 self.labels = list(prev_labels) + [label]
120 self.debug = debug
121
122 def add_label(self, label):
123 """Add a label to the writer, and return a new `LabelledDebug`."""
124 return LabelledDebug(label, self.debug, self.labels)
125
126 def message_prefix(self):
127 """The prefix to use on messages, combining the labels."""
128 prefixes = self.labels + ['']
129 return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
130
131 def write(self, message):
132 """Write `message`, but with the labels prepended."""
133 self.debug.write("%s%s" % (self.message_prefix(), message))
134
135
136 class DebugPluginWrapper(CoveragePlugin):
137 """Wrap a plugin, and use debug to report on what it's doing."""
138
139 def __init__(self, plugin, debug):
140 super(DebugPluginWrapper, self).__init__()
141 self.plugin = plugin
142 self.debug = debug
143
144 def file_tracer(self, filename):
145 tracer = self.plugin.file_tracer(filename)
146 self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
147 if tracer:
148 debug = self.debug.add_label("file %r" % (filename,))
149 tracer = DebugFileTracerWrapper(tracer, debug)
150 return tracer
151
152 def file_reporter(self, filename):
153 reporter = self.plugin.file_reporter(filename)
154 self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
155 if reporter:
156 debug = self.debug.add_label("file %r" % (filename,))
157 reporter = DebugFileReporterWrapper(filename, reporter, debug)
158 return reporter
159
160 def sys_info(self):
161 return self.plugin.sys_info()
162
163
164 class DebugFileTracerWrapper(FileTracer):
165 """A debugging `FileTracer`."""
166
167 def __init__(self, tracer, debug):
168 self.tracer = tracer
169 self.debug = debug
170
171 def _show_frame(self, frame):
172 """A short string identifying a frame, for debug messages."""
173 return "%s@%d" % (
174 os.path.basename(frame.f_code.co_filename),
175 frame.f_lineno,
176 )
177
178 def source_filename(self):
179 sfilename = self.tracer.source_filename()
180 self.debug.write("source_filename() --> %r" % (sfilename,))
181 return sfilename
182
183 def has_dynamic_source_filename(self):
184 has = self.tracer.has_dynamic_source_filename()
185 self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
186 return has
187
188 def dynamic_source_filename(self, filename, frame):
189 dyn = self.tracer.dynamic_source_filename(filename, frame)
190 self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
191 filename, self._show_frame(frame), dyn,
192 ))
193 return dyn
194
195 def line_number_range(self, frame):
196 pair = self.tracer.line_number_range(frame)
197 self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
198 return pair
199
200
201 class DebugFileReporterWrapper(FileReporter):
202 """A debugging `FileReporter`."""
203
204 def __init__(self, filename, reporter, debug):
205 super(DebugFileReporterWrapper, self).__init__(filename)
206 self.reporter = reporter
207 self.debug = debug
208
209 def relative_filename(self):
210 ret = self.reporter.relative_filename()
211 self.debug.write("relative_filename() --> %r" % (ret,))
212 return ret
213
214 def lines(self):
215 ret = self.reporter.lines()
216 self.debug.write("lines() --> %r" % (ret,))
217 return ret
218
219 def excluded_lines(self):
220 ret = self.reporter.excluded_lines()
221 self.debug.write("excluded_lines() --> %r" % (ret,))
222 return ret
223
224 def translate_lines(self, lines):
225 ret = self.reporter.translate_lines(lines)
226 self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
227 return ret
228
229 def translate_arcs(self, arcs):
230 ret = self.reporter.translate_arcs(arcs)
231 self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
232 return ret
233
234 def no_branch_lines(self):
235 ret = self.reporter.no_branch_lines()
236 self.debug.write("no_branch_lines() --> %r" % (ret,))
237 return ret
238
239 def exit_counts(self):
240 ret = self.reporter.exit_counts()
241 self.debug.write("exit_counts() --> %r" % (ret,))
242 return ret
243
244 def arcs(self):
245 ret = self.reporter.arcs()
246 self.debug.write("arcs() --> %r" % (ret,))
247 return ret
248
249 def source(self):
250 ret = self.reporter.source()
251 self.debug.write("source() --> %d chars" % (len(ret),))
252 return ret
253
254 def source_token_lines(self):
255 ret = list(self.reporter.source_token_lines())
256 self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
257 return ret

eric ide

mercurial