DebugClients/Python/coverage/plugin_support.py

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

eric ide

mercurial