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 |