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 """Raw data collector for coverage.py.""" |
4 """Raw data collector for coverage.py.""" |
5 |
5 |
6 import os, sys |
6 import os |
|
7 import sys |
7 |
8 |
8 from coverage import env |
9 from coverage import env |
9 from coverage.backward import iitems |
10 from coverage.backward import iitems |
10 from coverage.files import abs_file |
11 from coverage.files import abs_file |
11 from coverage.misc import CoverageException |
12 from coverage.misc import CoverageException, isolate_module |
12 from coverage.pytracer import PyTracer |
13 from coverage.pytracer import PyTracer |
|
14 |
|
15 os = isolate_module(os) |
|
16 |
13 |
17 |
14 try: |
18 try: |
15 # Use the C extension code when we can, for speed. |
19 # Use the C extension code when we can, for speed. |
16 from coverage.tracer import CTracer, CFileDisposition # pylint: disable=no-name-in-module |
20 from coverage.tracer import CTracer, CFileDisposition # pylint: disable=no-name-in-module |
17 except ImportError: |
21 except ImportError: |
29 |
33 |
30 |
34 |
31 class FileDisposition(object): |
35 class FileDisposition(object): |
32 """A simple value type for recording what to do with a file.""" |
36 """A simple value type for recording what to do with a file.""" |
33 pass |
37 pass |
|
38 |
|
39 |
|
40 def should_start_context(frame): |
|
41 """Who-Tests-What hack: Determine whether this frame begins a new who-context.""" |
|
42 fn_name = frame.f_code.co_name |
|
43 if fn_name.startswith("test"): |
|
44 return fn_name |
34 |
45 |
35 |
46 |
36 class Collector(object): |
47 class Collector(object): |
37 """Collects trace data. |
48 """Collects trace data. |
38 |
49 |
110 except ImportError: |
121 except ImportError: |
111 raise CoverageException( |
122 raise CoverageException( |
112 "Couldn't trace with concurrency=%s, the module isn't installed." % concurrency |
123 "Couldn't trace with concurrency=%s, the module isn't installed." % concurrency |
113 ) |
124 ) |
114 |
125 |
|
126 # Who-Tests-What is just a hack at the moment, so turn it on with an |
|
127 # environment variable. |
|
128 self.wtw = int(os.getenv('COVERAGE_WTW', 0)) |
|
129 |
115 self.reset() |
130 self.reset() |
116 |
131 |
117 if timid: |
132 if timid: |
118 # Being timid: use the simple Python trace function. |
133 # Being timid: use the simple Python trace function. |
119 self._trace_class = PyTracer |
134 self._trace_class = PyTracer |
140 """Clear collected data, and prepare to collect more.""" |
155 """Clear collected data, and prepare to collect more.""" |
141 # A dictionary mapping file names to dicts with line number keys (if not |
156 # A dictionary mapping file names to dicts with line number keys (if not |
142 # branch coverage), or mapping file names to dicts with line number |
157 # branch coverage), or mapping file names to dicts with line number |
143 # pairs as keys (if branch coverage). |
158 # pairs as keys (if branch coverage). |
144 self.data = {} |
159 self.data = {} |
|
160 |
|
161 # A dict mapping contexts to data dictionaries. |
|
162 self.contexts = {} |
|
163 self.contexts[None] = self.data |
145 |
164 |
146 # A dictionary mapping file names to file tracer plugin names that will |
165 # A dictionary mapping file names to file tracer plugin names that will |
147 # handle them. |
166 # handle them. |
148 self.file_tracers = {} |
167 self.file_tracers = {} |
149 |
168 |
200 tracer.file_tracers = self.file_tracers |
219 tracer.file_tracers = self.file_tracers |
201 if hasattr(tracer, 'threading'): |
220 if hasattr(tracer, 'threading'): |
202 tracer.threading = self.threading |
221 tracer.threading = self.threading |
203 if hasattr(tracer, 'check_include'): |
222 if hasattr(tracer, 'check_include'): |
204 tracer.check_include = self.check_include |
223 tracer.check_include = self.check_include |
|
224 if self.wtw: |
|
225 if hasattr(tracer, 'should_start_context'): |
|
226 tracer.should_start_context = should_start_context |
|
227 if hasattr(tracer, 'switch_context'): |
|
228 tracer.switch_context = self.switch_context |
205 |
229 |
206 fn = tracer.start() |
230 fn = tracer.start() |
207 self.tracers.append(tracer) |
231 self.tracers.append(tracer) |
208 |
232 |
209 return fn |
233 return fn |
288 tracer.stop() |
312 tracer.stop() |
289 stats = tracer.get_stats() |
313 stats = tracer.get_stats() |
290 if stats: |
314 if stats: |
291 print("\nCoverage.py tracer stats:") |
315 print("\nCoverage.py tracer stats:") |
292 for k in sorted(stats.keys()): |
316 for k in sorted(stats.keys()): |
293 print("%16s: %s" % (k, stats[k])) |
317 print("%20s: %s" % (k, stats[k])) |
294 if self.threading: |
318 if self.threading: |
295 self.threading.settrace(None) |
319 self.threading.settrace(None) |
296 |
320 |
297 def resume(self): |
321 def resume(self): |
298 """Resume tracing after a `pause`.""" |
322 """Resume tracing after a `pause`.""" |
301 if self.threading: |
325 if self.threading: |
302 self.threading.settrace(self._installation_trace) |
326 self.threading.settrace(self._installation_trace) |
303 else: |
327 else: |
304 self._start_tracer() |
328 self._start_tracer() |
305 |
329 |
|
330 def switch_context(self, new_context): |
|
331 """Who-Tests-What hack: switch to a new who-context.""" |
|
332 # Make a new data dict, or find the existing one, and switch all the |
|
333 # tracers to use it. |
|
334 data = self.contexts.setdefault(new_context, {}) |
|
335 for tracer in self.tracers: |
|
336 tracer.data = data |
|
337 |
306 def save_data(self, covdata): |
338 def save_data(self, covdata): |
307 """Save the collected data to a `CoverageData`. |
339 """Save the collected data to a `CoverageData`. |
308 |
340 |
309 Also resets the collector. |
341 Also resets the collector. |
310 |
342 |
317 covdata.add_arcs(abs_file_dict(self.data)) |
349 covdata.add_arcs(abs_file_dict(self.data)) |
318 else: |
350 else: |
319 covdata.add_lines(abs_file_dict(self.data)) |
351 covdata.add_lines(abs_file_dict(self.data)) |
320 covdata.add_file_tracers(abs_file_dict(self.file_tracers)) |
352 covdata.add_file_tracers(abs_file_dict(self.file_tracers)) |
321 |
353 |
|
354 if self.wtw: |
|
355 # Just a hack, so just hack it. |
|
356 import pprint |
|
357 out_file = "coverage_wtw_{:06}.py".format(os.getpid()) |
|
358 with open(out_file, "w") as wtw_out: |
|
359 pprint.pprint(self.contexts, wtw_out) |
|
360 |
322 self.reset() |
361 self.reset() |