PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/coverage/plugin_support.py

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