DebugClients/Python/coverage/plugin.py

changeset 6219
d6c795b5ce33
parent 5178
878ce843ca9f
equal deleted inserted replaced
6218:bedab77d0fa3 6219:d6c795b5ce33
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 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 2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3 3
4 """Plugin interfaces for coverage.py""" 4 """
5 .. versionadded:: 4.0
6
7 Plug-in interfaces for coverage.py.
8
9 Coverage.py supports a few different kinds of plug-ins that change its
10 behavior:
11
12 * File tracers implement tracing of non-Python file types.
13
14 * Configurers add custom configuration, using Python code to change the
15 configuration.
16
17 To write a coverage.py plug-in, create a module with a subclass of
18 :class:`~coverage.CoveragePlugin`. You will override methods in your class to
19 participate in various aspects of coverage.py's processing.
20 Different types of plug-ins have to override different methods.
21
22 Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
23 to provide debugging information about their operation.
24
25 Your module must also contain a ``coverage_init`` function that registers an
26 instance of your plug-in class::
27
28 import coverage
29
30 class MyPlugin(coverage.CoveragePlugin):
31 ...
32
33 def coverage_init(reg, options):
34 reg.add_file_tracer(MyPlugin())
35
36 You use the `reg` parameter passed to your ``coverage_init`` function to
37 register your plug-in object. The registration method you call depends on
38 what kind of plug-in it is.
39
40 If your plug-in takes options, the `options` parameter is a dictionary of your
41 plug-in's options from the coverage.py configuration file. Use them however
42 you want to configure your object before registering it.
43
44 Coverage.py will store its own information on your plug-in object, using
45 attributes whose names start with ``_coverage_``. Don't be startled.
46
47 .. warning::
48 Plug-ins are imported by coverage.py before it begins measuring code.
49 If you write a plugin in your own project, it might import your product
50 code before coverage.py can start measuring. This can result in your
51 own code being reported as missing.
52
53 One solution is to put your plugins in your project tree, but not in
54 your importable Python package.
55
56
57 File Tracers
58 ============
59
60 File tracers implement measurement support for non-Python files. File tracers
61 implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
62 files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
63 on those files.
64
65 In your ``coverage_init`` function, use the ``add_file_tracer`` method to
66 register your file tracer.
67
68
69 Configurers
70 ===========
71
72 .. versionadded:: 4.5
73
74 Configurers modify the configuration of coverage.py during start-up.
75 Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
76 change the configuration.
77
78 In your ``coverage_init`` function, use the ``add_configurer`` method to
79 register your configurer.
80
81 """
5 82
6 from coverage import files 83 from coverage import files
7 from coverage.misc import contract, _needs_to_implement 84 from coverage.misc import contract, _needs_to_implement
8 85
9 86
10 class CoveragePlugin(object): 87 class CoveragePlugin(object):
11 """Base class for coverage.py plugins. 88 """Base class for coverage.py plug-ins."""
12
13 To write a coverage.py plugin, create a module with a subclass of
14 :class:`CoveragePlugin`. You will override methods in your class to
15 participate in various aspects of coverage.py's processing.
16
17 Currently the only plugin type is a file tracer, for implementing
18 measurement support for non-Python files. File tracer plugins implement
19 the :meth:`file_tracer` method to claim files and the :meth:`file_reporter`
20 method to report on those files.
21
22 Any plugin can optionally implement :meth:`sys_info` to provide debugging
23 information about their operation.
24
25 Coverage.py will store its own information on your plugin object, using
26 attributes whose names start with ``_coverage_``. Don't be startled.
27
28 To register your plugin, define a function called `coverage_init` in your
29 module::
30
31 def coverage_init(reg, options):
32 reg.add_file_tracer(MyPlugin())
33
34 You use the `reg` parameter passed to your `coverage_init` function to
35 register your plugin object. It has one method, `add_file_tracer`, which
36 takes a newly created instance of your plugin.
37
38 If your plugin takes options, the `options` parameter is a dictionary of
39 your plugin's options from the coverage.py configuration file. Use them
40 however you want to configure your object before registering it.
41
42 """
43 89
44 def file_tracer(self, filename): # pylint: disable=unused-argument 90 def file_tracer(self, filename): # pylint: disable=unused-argument
45 """Get a :class:`FileTracer` object for a file. 91 """Get a :class:`FileTracer` object for a file.
46 92
47 Every Python source file is offered to the plugin to give it a chance 93 Plug-in type: file tracer.
48 to take responsibility for tracing the file. If your plugin can handle 94
49 the file, then return a :class:`FileTracer` object. Otherwise return 95 Every Python source file is offered to your plug-in to give it a chance
50 None. 96 to take responsibility for tracing the file. If your plug-in can
51 97 handle the file, then return a :class:`FileTracer` object. Otherwise
52 There is no way to register your plugin for particular files. Instead, 98 return None.
53 this method is invoked for all files, and the plugin decides whether it 99
54 can trace the file or not. Be prepared for `filename` to refer to all 100 There is no way to register your plug-in for particular files.
55 kinds of files that have nothing to do with your plugin. 101 Instead, this method is invoked for all files, and the plug-in decides
102 whether it can trace the file or not. Be prepared for `filename` to
103 refer to all kinds of files that have nothing to do with your plug-in.
56 104
57 The file name will be a Python file being executed. There are two 105 The file name will be a Python file being executed. There are two
58 broad categories of behavior for a plugin, depending on the kind of 106 broad categories of behavior for a plug-in, depending on the kind of
59 files your plugin supports: 107 files your plug-in supports:
60 108
61 * Static file names: each of your original source files has been 109 * Static file names: each of your original source files has been
62 converted into a distinct Python file. Your plugin is invoked with 110 converted into a distinct Python file. Your plug-in is invoked with
63 the Python file name, and it maps it back to its original source 111 the Python file name, and it maps it back to its original source
64 file. 112 file.
65 113
66 * Dynamic file names: all of your source files are executed by the same 114 * Dynamic file names: all of your source files are executed by the same
67 Python file. In this case, your plugin implements 115 Python file. In this case, your plug-in implements
68 :meth:`FileTracer.dynamic_source_filename` to provide the actual 116 :meth:`FileTracer.dynamic_source_filename` to provide the actual
69 source file for each execution frame. 117 source file for each execution frame.
70 118
71 `filename` is a string, the path to the file being considered. This is 119 `filename` is a string, the path to the file being considered. This is
72 the absolute real path to the file. If you are comparing to other 120 the absolute real path to the file. If you are comparing to other
73 paths, be sure to take this into account. 121 paths, be sure to take this into account.
74 122
75 Returns a :class:`FileTracer` object to use to trace `filename`, or 123 Returns a :class:`FileTracer` object to use to trace `filename`, or
76 None if this plugin cannot trace this file. 124 None if this plug-in cannot trace this file.
77 125
78 """ 126 """
79 return None 127 return None
80 128
81 def file_reporter(self, filename): # pylint: disable=unused-argument 129 def file_reporter(self, filename): # pylint: disable=unused-argument
82 """Get the :class:`FileReporter` class to use for a file. 130 """Get the :class:`FileReporter` class to use for a file.
83 131
132 Plug-in type: file tracer.
133
84 This will only be invoked if `filename` returns non-None from 134 This will only be invoked if `filename` returns non-None from
85 :meth:`file_tracer`. It's an error to return None from this method. 135 :meth:`file_tracer`. It's an error to return None from this method.
86 136
87 Returns a :class:`FileReporter` object to use to report on `filename`. 137 Returns a :class:`FileReporter` object to use to report on `filename`.
88 138
89 """ 139 """
90 _needs_to_implement(self, "file_reporter") 140 _needs_to_implement(self, "file_reporter")
91 141
142 def find_executable_files(self, src_dir): # pylint: disable=unused-argument
143 """Yield all of the executable files in `src_dir`, recursively.
144
145 Plug-in type: file tracer.
146
147 Executability is a plug-in-specific property, but generally means files
148 which would have been considered for coverage analysis, had they been
149 included automatically.
150
151 Returns or yields a sequence of strings, the paths to files that could
152 have been executed, including files that had been executed.
153
154 """
155 return []
156
157 def configure(self, config):
158 """Modify the configuration of coverage.py.
159
160 Plug-in type: configurer.
161
162 This method is called during coverage.py start-up, to give your plug-in
163 a chance to change the configuration. The `config` parameter is an
164 object with :meth:`~coverage.Coverage.get_option` and
165 :meth:`~coverage.Coverage.set_option` methods. Do not call any other
166 methods on the `config` object.
167
168 """
169 pass
170
92 def sys_info(self): 171 def sys_info(self):
93 """Get a list of information useful for debugging. 172 """Get a list of information useful for debugging.
94 173
174 Plug-in type: any.
175
95 This method will be invoked for ``--debug=sys``. Your 176 This method will be invoked for ``--debug=sys``. Your
96 plugin can return any information it wants to be displayed. 177 plug-in can return any information it wants to be displayed.
97 178
98 Returns a list of pairs: `[(name, value), ...]`. 179 Returns a list of pairs: `[(name, value), ...]`.
99 180
100 """ 181 """
101 return [] 182 return []
102 183
103 184
104 class FileTracer(object): 185 class FileTracer(object):
105 """Support needed for files during the execution phase. 186 """Support needed for files during the execution phase.
187
188 File tracer plug-ins implement subclasses of FileTracer to return from
189 their :meth:`~CoveragePlugin.file_tracer` method.
106 190
107 You may construct this object from :meth:`CoveragePlugin.file_tracer` any 191 You may construct this object from :meth:`CoveragePlugin.file_tracer` any
108 way you like. A natural choice would be to pass the file name given to 192 way you like. A natural choice would be to pass the file name given to
109 `file_tracer`. 193 `file_tracer`.
110 194
116 """ 200 """
117 201
118 def source_filename(self): 202 def source_filename(self):
119 """The source file name for this file. 203 """The source file name for this file.
120 204
121 This may be any file name you like. A key responsibility of a plugin 205 This may be any file name you like. A key responsibility of a plug-in
122 is to own the mapping from Python execution back to whatever source 206 is to own the mapping from Python execution back to whatever source
123 file name was originally the source of the code. 207 file name was originally the source of the code.
124 208
125 See :meth:`CoveragePlugin.file_tracer` for details about static and 209 See :meth:`CoveragePlugin.file_tracer` for details about static and
126 dynamic file names. 210 dynamic file names.
149 return False 233 return False
150 234
151 def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument 235 def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
152 """Get a dynamically computed source file name. 236 """Get a dynamically computed source file name.
153 237
154 Some plugins need to compute the source file name dynamically for each 238 Some plug-ins need to compute the source file name dynamically for each
155 frame. 239 frame.
156 240
157 This function will not be invoked if 241 This function will not be invoked if
158 :meth:`has_dynamic_source_filename` returns False. 242 :meth:`has_dynamic_source_filename` returns False.
159 243
182 266
183 267
184 class FileReporter(object): 268 class FileReporter(object):
185 """Support needed for files during the analysis and reporting phases. 269 """Support needed for files during the analysis and reporting phases.
186 270
187 See :ref:`howitworks` for details of the different coverage.py phases. 271 File tracer plug-ins implement a subclass of `FileReporter`, and return
188 272 instances from their :meth:`CoveragePlugin.file_reporter` method.
189 `FileReporter` objects should only be created in the
190 :meth:`CoveragePlugin.file_reporter` method.
191 273
192 There are many methods here, but only :meth:`lines` is required, to provide 274 There are many methods here, but only :meth:`lines` is required, to provide
193 the set of executable lines in the file. 275 the set of executable lines in the file.
276
277 See :ref:`howitworks` for details of the different coverage.py phases.
194 278
195 """ 279 """
196 280
197 def __init__(self, filename): 281 def __init__(self, filename):
198 """Simple initialization of a `FileReporter`. 282 """Simple initialization of a `FileReporter`.
233 return f.read().decode("utf8") 317 return f.read().decode("utf8")
234 318
235 def lines(self): 319 def lines(self):
236 """Get the executable lines in this file. 320 """Get the executable lines in this file.
237 321
238 Your plugin must determine which lines in the file were possibly 322 Your plug-in must determine which lines in the file were possibly
239 executable. This method returns a set of those line numbers. 323 executable. This method returns a set of those line numbers.
240 324
241 Returns a set of line numbers. 325 Returns a set of line numbers.
242 326
243 """ 327 """
244 _needs_to_implement(self, "lines") 328 _needs_to_implement(self, "lines")
245 329
246 def excluded_lines(self): 330 def excluded_lines(self):
247 """Get the excluded executable lines in this file. 331 """Get the excluded executable lines in this file.
248 332
249 Your plugin can use any method it likes to allow the user to exclude 333 Your plug-in can use any method it likes to allow the user to exclude
250 executable lines from consideration. 334 executable lines from consideration.
251 335
252 Returns a set of line numbers. 336 Returns a set of line numbers.
253 337
254 The base implementation returns the empty set. 338 The base implementation returns the empty set.
262 Some file formats will want to report lines slightly differently than 346 Some file formats will want to report lines slightly differently than
263 they are recorded. For example, Python records the last line of a 347 they are recorded. For example, Python records the last line of a
264 multi-line statement, but reports are nicer if they mention the first 348 multi-line statement, but reports are nicer if they mention the first
265 line. 349 line.
266 350
267 Your plugin can optionally define this method to perform these kinds of 351 Your plug-in can optionally define this method to perform these kinds
268 adjustment. 352 of adjustment.
269 353
270 `lines` is a sequence of integers, the recorded line numbers. 354 `lines` is a sequence of integers, the recorded line numbers.
271 355
272 Returns a set of integers, the adjusted line numbers. 356 Returns a set of integers, the adjusted line numbers.
273 357
277 return set(lines) 361 return set(lines)
278 362
279 def arcs(self): 363 def arcs(self):
280 """Get the executable arcs in this file. 364 """Get the executable arcs in this file.
281 365
282 To support branch coverage, your plugin needs to be able to indicate 366 To support branch coverage, your plug-in needs to be able to indicate
283 possible execution paths, as a set of line number pairs. Each pair is 367 possible execution paths, as a set of line number pairs. Each pair is
284 a `(prev, next)` pair indicating that execution can transition from the 368 a `(prev, next)` pair indicating that execution can transition from the
285 `prev` line number to the `next` line number. 369 `prev` line number to the `next` line number.
286 370
287 Returns a set of pairs of line numbers. The default implementation 371 Returns a set of pairs of line numbers. The default implementation
291 return set() 375 return set()
292 376
293 def no_branch_lines(self): 377 def no_branch_lines(self):
294 """Get the lines excused from branch coverage in this file. 378 """Get the lines excused from branch coverage in this file.
295 379
296 Your plugin can use any method it likes to allow the user to exclude 380 Your plug-in can use any method it likes to allow the user to exclude
297 lines from consideration of branch coverage. 381 lines from consideration of branch coverage.
298 382
299 Returns a set of line numbers. 383 Returns a set of line numbers.
300 384
301 The base implementation returns the empty set. 385 The base implementation returns the empty set.
322 To determine which lines are branches, coverage.py looks for lines that 406 To determine which lines are branches, coverage.py looks for lines that
323 have more than one exit. This function creates a dict mapping each 407 have more than one exit. This function creates a dict mapping each
324 executable line number to a count of how many exits it has. 408 executable line number to a count of how many exits it has.
325 409
326 To be honest, this feels wrong, and should be refactored. Let me know 410 To be honest, this feels wrong, and should be refactored. Let me know
327 if you attempt to implement this method in your plugin... 411 if you attempt to implement this method in your plug-in...
328 412
329 """ 413 """
330 return {} 414 return {}
331 415
332 def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument 416 def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
392 def __gt__(self, other): 476 def __gt__(self, other):
393 return self.filename > other.filename 477 return self.filename > other.filename
394 478
395 def __ge__(self, other): 479 def __ge__(self, other):
396 return self.filename >= other.filename 480 return self.filename >= other.filename
481
482 __hash__ = None # This object doesn't need to be hashed.

eric ide

mercurial