OLD | NEW |
1 """Core control stuff for Coverage.""" | 1 """Core control stuff for Coverage.""" |
2 | 2 |
3 import atexit, os, random, socket, sys | 3 import atexit, os, random, socket, sys |
4 | 4 |
5 from coverage.annotate import AnnotateReporter | 5 from coverage.annotate import AnnotateReporter |
6 from coverage.backward import string_class, iitems | 6 from coverage.backward import string_class, iitems, sorted # pylint: disable=W0
622 |
7 from coverage.codeunit import code_unit_factory, CodeUnit | 7 from coverage.codeunit import code_unit_factory, CodeUnit |
8 from coverage.collector import Collector | 8 from coverage.collector import Collector |
9 from coverage.config import CoverageConfig | 9 from coverage.config import CoverageConfig |
10 from coverage.data import CoverageData | 10 from coverage.data import CoverageData |
| 11 from coverage.debug import DebugControl |
11 from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher | 12 from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher |
12 from coverage.files import PathAliases, find_python_files, prep_patterns | 13 from coverage.files import PathAliases, find_python_files, prep_patterns |
13 from coverage.html import HtmlReporter | 14 from coverage.html import HtmlReporter |
14 from coverage.misc import CoverageException, bool_or_none, join_regex | 15 from coverage.misc import CoverageException, bool_or_none, join_regex |
15 from coverage.misc import file_be_gone | 16 from coverage.misc import file_be_gone |
16 from coverage.results import Analysis, Numbers | 17 from coverage.results import Analysis, Numbers |
17 from coverage.summary import SummaryReporter | 18 from coverage.summary import SummaryReporter |
18 from coverage.xmlreport import XmlReporter | 19 from coverage.xmlreport import XmlReporter |
19 | 20 |
| 21 # Pypy has some unusual stuff in the "stdlib". Consider those locations |
| 22 # when deciding where the stdlib is. |
| 23 try: |
| 24 import _structseq # pylint: disable=F0401 |
| 25 except ImportError: |
| 26 _structseq = None |
| 27 |
| 28 |
20 class coverage(object): | 29 class coverage(object): |
21 """Programmatic access to coverage.py. | 30 """Programmatic access to coverage.py. |
22 | 31 |
23 To use:: | 32 To use:: |
24 | 33 |
25 from coverage import coverage | 34 from coverage import coverage |
26 | 35 |
27 cov = coverage() | 36 cov = coverage() |
28 cov.start() | 37 cov.start() |
29 #.. call your code .. | 38 #.. call your code .. |
30 cov.stop() | 39 cov.stop() |
31 cov.html_report(directory='covhtml') | 40 cov.html_report(directory='covhtml') |
32 | 41 |
33 """ | 42 """ |
34 def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, | 43 def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, |
35 auto_data=False, timid=None, branch=None, config_file=True, | 44 auto_data=False, timid=None, branch=None, config_file=True, |
36 source=None, omit=None, include=None): | 45 source=None, omit=None, include=None, debug=None, |
| 46 debug_file=None): |
37 """ | 47 """ |
38 `data_file` is the base name of the data file to use, defaulting to | 48 `data_file` is the base name of the data file to use, defaulting to |
39 ".coverage". `data_suffix` is appended (with a dot) to `data_file` to | 49 ".coverage". `data_suffix` is appended (with a dot) to `data_file` to |
40 create the final file name. If `data_suffix` is simply True, then a | 50 create the final file name. If `data_suffix` is simply True, then a |
41 suffix is created with the machine and process identity included. | 51 suffix is created with the machine and process identity included. |
42 | 52 |
43 `cover_pylib` is a boolean determining whether Python code installed | 53 `cover_pylib` is a boolean determining whether Python code installed |
44 with the Python interpreter is measured. This includes the Python | 54 with the Python interpreter is measured. This includes the Python |
45 standard library and any packages installed with the interpreter. | 55 standard library and any packages installed with the interpreter. |
46 | 56 |
(...skipping 14 matching lines...) Expand all Loading... |
61 read. | 71 read. |
62 | 72 |
63 `source` is a list of file paths or package names. Only code located | 73 `source` is a list of file paths or package names. Only code located |
64 in the trees indicated by the file paths or package names will be | 74 in the trees indicated by the file paths or package names will be |
65 measured. | 75 measured. |
66 | 76 |
67 `include` and `omit` are lists of filename patterns. Files that match | 77 `include` and `omit` are lists of filename patterns. Files that match |
68 `include` will be measured, files that match `omit` will not. Each | 78 `include` will be measured, files that match `omit` will not. Each |
69 will also accept a single string argument. | 79 will also accept a single string argument. |
70 | 80 |
| 81 `debug` is a list of strings indicating what debugging information is |
| 82 desired. `debug_file` is the file to write debug messages to, |
| 83 defaulting to stderr. |
| 84 |
71 """ | 85 """ |
72 from coverage import __version__ | 86 from coverage import __version__ |
73 | 87 |
74 # A record of all the warnings that have been issued. | 88 # A record of all the warnings that have been issued. |
75 self._warnings = [] | 89 self._warnings = [] |
76 | 90 |
77 # Build our configuration from a number of sources: | 91 # Build our configuration from a number of sources: |
78 # 1: defaults: | 92 # 1: defaults: |
79 self.config = CoverageConfig() | 93 self.config = CoverageConfig() |
80 | 94 |
(...skipping 12 matching lines...) Expand all Loading... |
93 # 3: from environment variables: | 107 # 3: from environment variables: |
94 self.config.from_environment('COVERAGE_OPTIONS') | 108 self.config.from_environment('COVERAGE_OPTIONS') |
95 env_data_file = os.environ.get('COVERAGE_FILE') | 109 env_data_file = os.environ.get('COVERAGE_FILE') |
96 if env_data_file: | 110 if env_data_file: |
97 self.config.data_file = env_data_file | 111 self.config.data_file = env_data_file |
98 | 112 |
99 # 4: from constructor arguments: | 113 # 4: from constructor arguments: |
100 self.config.from_args( | 114 self.config.from_args( |
101 data_file=data_file, cover_pylib=cover_pylib, timid=timid, | 115 data_file=data_file, cover_pylib=cover_pylib, timid=timid, |
102 branch=branch, parallel=bool_or_none(data_suffix), | 116 branch=branch, parallel=bool_or_none(data_suffix), |
103 source=source, omit=omit, include=include | 117 source=source, omit=omit, include=include, debug=debug, |
104 ) | 118 ) |
105 | 119 |
| 120 # Create and configure the debugging controller. |
| 121 self.debug = DebugControl(self.config.debug, debug_file or sys.stderr) |
| 122 |
106 self.auto_data = auto_data | 123 self.auto_data = auto_data |
107 | 124 |
108 # _exclude_re is a dict mapping exclusion list names to compiled | 125 # _exclude_re is a dict mapping exclusion list names to compiled |
109 # regexes. | 126 # regexes. |
110 self._exclude_re = {} | 127 self._exclude_re = {} |
111 self._exclude_regex_stale() | 128 self._exclude_regex_stale() |
112 | 129 |
113 self.file_locator = FileLocator() | 130 self.file_locator = FileLocator() |
114 | 131 |
115 # The source argument can be directories or package names. | 132 # The source argument can be directories or package names. |
(...skipping 24 matching lines...) Expand all Loading... |
140 else: | 157 else: |
141 data_suffix = None | 158 data_suffix = None |
142 self.data_suffix = None | 159 self.data_suffix = None |
143 self.run_suffix = data_suffix | 160 self.run_suffix = data_suffix |
144 | 161 |
145 # Create the data file. We do this at construction time so that the | 162 # Create the data file. We do this at construction time so that the |
146 # data file will be written into the directory where the process | 163 # data file will be written into the directory where the process |
147 # started rather than wherever the process eventually chdir'd to. | 164 # started rather than wherever the process eventually chdir'd to. |
148 self.data = CoverageData( | 165 self.data = CoverageData( |
149 basename=self.config.data_file, | 166 basename=self.config.data_file, |
150 collector="coverage v%s" % __version__ | 167 collector="coverage v%s" % __version__, |
| 168 debug=self.debug, |
151 ) | 169 ) |
152 | 170 |
153 # The dirs for files considered "installed with the interpreter". | 171 # The dirs for files considered "installed with the interpreter". |
154 self.pylib_dirs = [] | 172 self.pylib_dirs = [] |
155 if not self.config.cover_pylib: | 173 if not self.config.cover_pylib: |
156 # Look at where some standard modules are located. That's the | 174 # Look at where some standard modules are located. That's the |
157 # indication for "installed with the interpreter". In some | 175 # indication for "installed with the interpreter". In some |
158 # environments (virtualenv, for example), these modules may be | 176 # environments (virtualenv, for example), these modules may be |
159 # spread across a few locations. Look at all the candidate modules | 177 # spread across a few locations. Look at all the candidate modules |
160 # we've imported, and take all the different ones. | 178 # we've imported, and take all the different ones. |
161 for m in (atexit, os, random, socket): | 179 for m in (atexit, os, random, socket, _structseq): |
162 if hasattr(m, "__file__"): | 180 if m is not None and hasattr(m, "__file__"): |
163 m_dir = self._canonical_dir(m) | 181 m_dir = self._canonical_dir(m) |
164 if m_dir not in self.pylib_dirs: | 182 if m_dir not in self.pylib_dirs: |
165 self.pylib_dirs.append(m_dir) | 183 self.pylib_dirs.append(m_dir) |
166 | 184 |
167 # To avoid tracing the coverage code itself, we skip anything located | 185 # To avoid tracing the coverage code itself, we skip anything located |
168 # where we are. | 186 # where we are. |
169 self.cover_dir = self._canonical_dir(__file__) | 187 self.cover_dir = self._canonical_dir(__file__) |
170 | 188 |
171 # The matchers for _should_trace, created when tracing starts. | 189 # The matchers for _should_trace. |
172 self.source_match = None | 190 self.source_match = None |
173 self.pylib_match = self.cover_match = None | 191 self.pylib_match = self.cover_match = None |
174 self.include_match = self.omit_match = None | 192 self.include_match = self.omit_match = None |
175 | 193 |
176 # Set the reporting precision. | 194 # Set the reporting precision. |
177 Numbers.set_precision(self.config.precision) | 195 Numbers.set_precision(self.config.precision) |
178 | 196 |
179 # Is it ok for no data to be collected? | 197 # Is it ok for no data to be collected? |
180 self._warn_no_data = True | 198 self._warn_no_data = True |
181 self._warn_unimported_source = True | 199 self._warn_unimported_source = True |
(...skipping 12 matching lines...) Expand all Loading... |
194 | 212 |
195 def _source_for_file(self, filename): | 213 def _source_for_file(self, filename): |
196 """Return the source file for `filename`.""" | 214 """Return the source file for `filename`.""" |
197 if not filename.endswith(".py"): | 215 if not filename.endswith(".py"): |
198 if filename[-4:-1] == ".py": | 216 if filename[-4:-1] == ".py": |
199 filename = filename[:-1] | 217 filename = filename[:-1] |
200 elif filename.endswith("$py.class"): # jython | 218 elif filename.endswith("$py.class"): # jython |
201 filename = filename[:-9] + ".py" | 219 filename = filename[:-9] + ".py" |
202 return filename | 220 return filename |
203 | 221 |
204 def _should_trace(self, filename, frame): | 222 def _should_trace_with_reason(self, filename, frame): |
205 """Decide whether to trace execution in `filename` | 223 """Decide whether to trace execution in `filename`, with a reason. |
206 | 224 |
207 This function is called from the trace function. As each new file name | 225 This function is called from the trace function. As each new file name |
208 is encountered, this function determines whether it is traced or not. | 226 is encountered, this function determines whether it is traced or not. |
209 | 227 |
210 Returns a canonicalized filename if it should be traced, False if it | 228 Returns a pair of values: the first indicates whether the file should |
211 should not. | 229 be traced: it's a canonicalized filename if it should be traced, None |
| 230 if it should not. The second value is a string, the resason for the |
| 231 decision. |
212 | 232 |
213 """ | 233 """ |
214 if not filename: | 234 if not filename: |
215 # Empty string is pretty useless | 235 # Empty string is pretty useless |
216 return False | 236 return None, "empty string isn't a filename" |
217 | 237 |
218 if filename.startswith('<'): | 238 if filename.startswith('<'): |
219 # Lots of non-file execution is represented with artificial | 239 # Lots of non-file execution is represented with artificial |
220 # filenames like "<string>", "<doctest readme.txt[0]>", or | 240 # filenames like "<string>", "<doctest readme.txt[0]>", or |
221 # "<exec_function>". Don't ever trace these executions, since we | 241 # "<exec_function>". Don't ever trace these executions, since we |
222 # can't do anything with the data later anyway. | 242 # can't do anything with the data later anyway. |
223 return False | 243 return None, "not a real filename" |
224 | 244 |
225 self._check_for_packages() | 245 self._check_for_packages() |
226 | 246 |
227 # Compiled Python files have two filenames: frame.f_code.co_filename is | 247 # Compiled Python files have two filenames: frame.f_code.co_filename is |
228 # the filename at the time the .pyc was compiled. The second name is | 248 # the filename at the time the .pyc was compiled. The second name is |
229 # __file__, which is where the .pyc was actually loaded from. Since | 249 # __file__, which is where the .pyc was actually loaded from. Since |
230 # .pyc files can be moved after compilation (for example, by being | 250 # .pyc files can be moved after compilation (for example, by being |
231 # installed), we look for __file__ in the frame and prefer it to the | 251 # installed), we look for __file__ in the frame and prefer it to the |
232 # co_filename value. | 252 # co_filename value. |
233 dunder_file = frame.f_globals.get('__file__') | 253 dunder_file = frame.f_globals.get('__file__') |
234 if dunder_file: | 254 if dunder_file: |
235 filename = self._source_for_file(dunder_file) | 255 filename = self._source_for_file(dunder_file) |
236 | 256 |
237 # Jython reports the .class file to the tracer, use the source file. | 257 # Jython reports the .class file to the tracer, use the source file. |
238 if filename.endswith("$py.class"): | 258 if filename.endswith("$py.class"): |
239 filename = filename[:-9] + ".py" | 259 filename = filename[:-9] + ".py" |
240 | 260 |
241 canonical = self.file_locator.canonical_filename(filename) | 261 canonical = self.file_locator.canonical_filename(filename) |
242 | 262 |
243 # If the user specified source or include, then that's authoritative | 263 # If the user specified source or include, then that's authoritative |
244 # about the outer bound of what to measure and we don't have to apply | 264 # about the outer bound of what to measure and we don't have to apply |
245 # any canned exclusions. If they didn't, then we have to exclude the | 265 # any canned exclusions. If they didn't, then we have to exclude the |
246 # stdlib and coverage.py directories. | 266 # stdlib and coverage.py directories. |
247 if self.source_match: | 267 if self.source_match: |
248 if not self.source_match.match(canonical): | 268 if not self.source_match.match(canonical): |
249 return False | 269 return None, "falls outside the --source trees" |
250 elif self.include_match: | 270 elif self.include_match: |
251 if not self.include_match.match(canonical): | 271 if not self.include_match.match(canonical): |
252 return False | 272 return None, "falls outside the --include trees" |
253 else: | 273 else: |
254 # If we aren't supposed to trace installed code, then check if this | 274 # If we aren't supposed to trace installed code, then check if this |
255 # is near the Python standard library and skip it if so. | 275 # is near the Python standard library and skip it if so. |
256 if self.pylib_match and self.pylib_match.match(canonical): | 276 if self.pylib_match and self.pylib_match.match(canonical): |
257 return False | 277 return None, "is in the stdlib" |
258 | 278 |
259 # We exclude the coverage code itself, since a little of it will be | 279 # We exclude the coverage code itself, since a little of it will be |
260 # measured otherwise. | 280 # measured otherwise. |
261 if self.cover_match and self.cover_match.match(canonical): | 281 if self.cover_match and self.cover_match.match(canonical): |
262 return False | 282 return None, "is part of coverage.py" |
263 | 283 |
264 # Check the file against the omit pattern. | 284 # Check the file against the omit pattern. |
265 if self.omit_match and self.omit_match.match(canonical): | 285 if self.omit_match and self.omit_match.match(canonical): |
266 return False | 286 return None, "is inside an --omit pattern" |
267 | 287 |
| 288 return canonical, "because we love you" |
| 289 |
| 290 def _should_trace(self, filename, frame): |
| 291 """Decide whether to trace execution in `filename`. |
| 292 |
| 293 Calls `_should_trace_with_reason`, and returns just the decision. |
| 294 |
| 295 """ |
| 296 canonical, reason = self._should_trace_with_reason(filename, frame) |
| 297 if self.debug.should('trace'): |
| 298 if not canonical: |
| 299 msg = "Not tracing %r: %s" % (filename, reason) |
| 300 else: |
| 301 msg = "Tracing %r" % (filename,) |
| 302 self.debug.write(msg) |
268 return canonical | 303 return canonical |
269 | 304 |
270 # To log what should_trace returns, change this to "if 1:" | |
271 if 0: | |
272 _real_should_trace = _should_trace | |
273 def _should_trace(self, filename, frame): # pylint: disable=E0102 | |
274 """A logging decorator around the real _should_trace function.""" | |
275 ret = self._real_should_trace(filename, frame) | |
276 print("should_trace: %r -> %r" % (filename, ret)) | |
277 return ret | |
278 | |
279 def _warn(self, msg): | 305 def _warn(self, msg): |
280 """Use `msg` as a warning.""" | 306 """Use `msg` as a warning.""" |
281 self._warnings.append(msg) | 307 self._warnings.append(msg) |
282 sys.stderr.write("Coverage.py warning: %s\n" % msg) | 308 sys.stderr.write("Coverage.py warning: %s\n" % msg) |
283 | 309 |
284 def _check_for_packages(self): | 310 def _check_for_packages(self): |
285 """Update the source_match matcher with latest imported packages.""" | 311 """Update the source_match matcher with latest imported packages.""" |
286 # Our self.source_pkgs attribute is a list of package names we want to | 312 # Our self.source_pkgs attribute is a list of package names we want to |
287 # measure. Each time through here, we see if we've imported any of | 313 # measure. Each time through here, we see if we've imported any of |
288 # them yet. If so, we add its file to source_match, and we don't have | 314 # them yet. If so, we add its file to source_match, and we don't have |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 else: | 383 else: |
358 if self.cover_dir: | 384 if self.cover_dir: |
359 self.cover_match = TreeMatcher([self.cover_dir]) | 385 self.cover_match = TreeMatcher([self.cover_dir]) |
360 if self.pylib_dirs: | 386 if self.pylib_dirs: |
361 self.pylib_match = TreeMatcher(self.pylib_dirs) | 387 self.pylib_match = TreeMatcher(self.pylib_dirs) |
362 if self.include: | 388 if self.include: |
363 self.include_match = FnmatchMatcher(self.include) | 389 self.include_match = FnmatchMatcher(self.include) |
364 if self.omit: | 390 if self.omit: |
365 self.omit_match = FnmatchMatcher(self.omit) | 391 self.omit_match = FnmatchMatcher(self.omit) |
366 | 392 |
| 393 # The user may want to debug things, show info if desired. |
| 394 if self.debug.should('config'): |
| 395 self.debug.write("Configuration values:") |
| 396 config_info = sorted(self.config.__dict__.items()) |
| 397 self.debug.write_formatted_info(config_info) |
| 398 |
| 399 if self.debug.should('sys'): |
| 400 self.debug.write("Debugging info:") |
| 401 self.debug.write_formatted_info(self.sysinfo()) |
| 402 |
367 self.collector.start() | 403 self.collector.start() |
368 self._started = True | 404 self._started = True |
369 self._measured = True | 405 self._measured = True |
370 | 406 |
371 def stop(self): | 407 def stop(self): |
372 """Stop measuring code coverage.""" | 408 """Stop measuring code coverage.""" |
373 self._started = False | 409 self._started = False |
374 self.collector.stop() | 410 self.collector.stop() |
375 | 411 |
376 def _atexit(self): | 412 def _atexit(self): |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 for pattern in paths[1:]: | 508 for pattern in paths[1:]: |
473 aliases.add(pattern, result) | 509 aliases.add(pattern, result) |
474 self.data.combine_parallel_data(aliases=aliases) | 510 self.data.combine_parallel_data(aliases=aliases) |
475 | 511 |
476 def _harvest_data(self): | 512 def _harvest_data(self): |
477 """Get the collected data and reset the collector. | 513 """Get the collected data and reset the collector. |
478 | 514 |
479 Also warn about various problems collecting data. | 515 Also warn about various problems collecting data. |
480 | 516 |
481 """ | 517 """ |
482 if self._measured: | 518 if not self._measured: |
483 self.data.add_line_data(self.collector.get_line_data()) | 519 return |
484 self.data.add_arc_data(self.collector.get_arc_data()) | |
485 self.collector.reset() | |
486 | 520 |
487 # If there are still entries in the source_pkgs list, then we never | 521 self.data.add_line_data(self.collector.get_line_data()) |
488 # encountered those packages. | 522 self.data.add_arc_data(self.collector.get_arc_data()) |
489 if self._warn_unimported_source: | 523 self.collector.reset() |
490 for pkg in self.source_pkgs: | |
491 self._warn("Module %s was never imported." % pkg) | |
492 | 524 |
493 # Find out if we got any data. | 525 # If there are still entries in the source_pkgs list, then we never |
494 summary = self.data.summary() | 526 # encountered those packages. |
495 if not summary and self._warn_no_data: | 527 if self._warn_unimported_source: |
496 self._warn("No data was collected.") | 528 for pkg in self.source_pkgs: |
| 529 self._warn("Module %s was never imported." % pkg) |
497 | 530 |
498 # Find files that were never executed at all. | 531 # Find out if we got any data. |
499 for src in self.source: | 532 summary = self.data.summary() |
500 for py_file in find_python_files(src): | 533 if not summary and self._warn_no_data: |
501 py_file = self.file_locator.canonical_filename(py_file) | 534 self._warn("No data was collected.") |
502 self.data.touch_file(py_file) | |
503 | 535 |
504 self._measured = False | 536 # Find files that were never executed at all. |
| 537 for src in self.source: |
| 538 for py_file in find_python_files(src): |
| 539 py_file = self.file_locator.canonical_filename(py_file) |
| 540 |
| 541 if self.omit_match and self.omit_match.match(py_file): |
| 542 # Turns out this file was omitted, so don't pull it back |
| 543 # in as unexecuted. |
| 544 continue |
| 545 |
| 546 self.data.touch_file(py_file) |
| 547 |
| 548 self._measured = False |
505 | 549 |
506 # Backward compatibility with version 1. | 550 # Backward compatibility with version 1. |
507 def analysis(self, morf): | 551 def analysis(self, morf): |
508 """Like `analysis2` but doesn't return excluded line numbers.""" | 552 """Like `analysis2` but doesn't return excluded line numbers.""" |
509 f, s, _, m, mf = self.analysis2(morf) | 553 f, s, _, m, mf = self.analysis2(morf) |
510 return f, s, m, mf | 554 return f, s, m, mf |
511 | 555 |
512 def analysis2(self, morf): | 556 def analysis2(self, morf): |
513 """Analyze a module. | 557 """Analyze a module. |
514 | 558 |
515 `morf` is a module or a filename. It will be analyzed to determine | 559 `morf` is a module or a filename. It will be analyzed to determine |
516 its coverage statistics. The return value is a 5-tuple: | 560 its coverage statistics. The return value is a 5-tuple: |
517 | 561 |
518 * The filename for the module. | 562 * The filename for the module. |
519 * A list of line numbers of executable statements. | 563 * A list of line numbers of executable statements. |
520 * A list of line numbers of excluded statements. | 564 * A list of line numbers of excluded statements. |
521 * A list of line numbers of statements not run (missing from | 565 * A list of line numbers of statements not run (missing from |
522 execution). | 566 execution). |
523 * A readable formatted string of the missing line numbers. | 567 * A readable formatted string of the missing line numbers. |
524 | 568 |
525 The analysis uses the source file itself and the current measured | 569 The analysis uses the source file itself and the current measured |
526 coverage data. | 570 coverage data. |
527 | 571 |
528 """ | 572 """ |
529 analysis = self._analyze(morf) | 573 analysis = self._analyze(morf) |
530 return ( | 574 return ( |
531 analysis.filename, analysis.statements, analysis.excluded, | 575 analysis.filename, |
532 analysis.missing, analysis.missing_formatted() | 576 sorted(analysis.statements), |
| 577 sorted(analysis.excluded), |
| 578 sorted(analysis.missing), |
| 579 analysis.missing_formatted(), |
533 ) | 580 ) |
534 | 581 |
535 def _analyze(self, it): | 582 def _analyze(self, it): |
536 """Analyze a single morf or code unit. | 583 """Analyze a single morf or code unit. |
537 | 584 |
538 Returns an `Analysis` object. | 585 Returns an `Analysis` object. |
539 | 586 |
540 """ | 587 """ |
541 self._harvest_data() | 588 self._harvest_data() |
542 if not isinstance(it, CodeUnit): | 589 if not isinstance(it, CodeUnit): |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
673 ('tracer', self.collector.tracer_name()), | 720 ('tracer', self.collector.tracer_name()), |
674 ('config_files', self.config.attempted_config_files), | 721 ('config_files', self.config.attempted_config_files), |
675 ('configs_read', self.config.config_files), | 722 ('configs_read', self.config.config_files), |
676 ('data_path', self.data.filename), | 723 ('data_path', self.data.filename), |
677 ('python', sys.version.replace('\n', '')), | 724 ('python', sys.version.replace('\n', '')), |
678 ('platform', platform.platform()), | 725 ('platform', platform.platform()), |
679 ('implementation', implementation), | 726 ('implementation', implementation), |
680 ('executable', sys.executable), | 727 ('executable', sys.executable), |
681 ('cwd', os.getcwd()), | 728 ('cwd', os.getcwd()), |
682 ('path', sys.path), | 729 ('path', sys.path), |
683 ('environment', [ | 730 ('environment', sorted([ |
684 ("%s = %s" % (k, v)) for k, v in iitems(os.environ) | 731 ("%s = %s" % (k, v)) for k, v in iitems(os.environ) |
685 if re.search(r"^COV|^PY", k) | 732 if re.search(r"^COV|^PY", k) |
686 ]), | 733 ])), |
| 734 ('command_line', " ".join(getattr(sys, 'argv', ['???']))), |
687 ] | 735 ] |
| 736 if self.source_match: |
| 737 info.append(('source_match', self.source_match.info())) |
| 738 if self.include_match: |
| 739 info.append(('include_match', self.include_match.info())) |
| 740 if self.omit_match: |
| 741 info.append(('omit_match', self.omit_match.info())) |
| 742 if self.cover_match: |
| 743 info.append(('cover_match', self.cover_match.info())) |
| 744 if self.pylib_match: |
| 745 info.append(('pylib_match', self.pylib_match.info())) |
| 746 |
688 return info | 747 return info |
689 | 748 |
690 | 749 |
691 def process_startup(): | 750 def process_startup(): |
692 """Call this at Python startup to perhaps measure coverage. | 751 """Call this at Python startup to perhaps measure coverage. |
693 | 752 |
694 If the environment variable COVERAGE_PROCESS_START is defined, coverage | 753 If the environment variable COVERAGE_PROCESS_START is defined, coverage |
695 measurement is started. The value of the variable is the config file | 754 measurement is started. The value of the variable is the config file |
696 to use. | 755 to use. |
697 | 756 |
(...skipping 13 matching lines...) Expand all Loading... |
711 cps = os.environ.get("COVERAGE_PROCESS_START") | 770 cps = os.environ.get("COVERAGE_PROCESS_START") |
712 if cps: | 771 if cps: |
713 cov = coverage(config_file=cps, auto_data=True) | 772 cov = coverage(config_file=cps, auto_data=True) |
714 cov.start() | 773 cov.start() |
715 cov._warn_no_data = False | 774 cov._warn_no_data = False |
716 cov._warn_unimported_source = False | 775 cov._warn_unimported_source = False |
717 | 776 |
718 | 777 |
719 # A hack for debugging testing in subprocesses. | 778 # A hack for debugging testing in subprocesses. |
720 _TEST_NAME_FILE = "" #"/tmp/covtest.txt" | 779 _TEST_NAME_FILE = "" #"/tmp/covtest.txt" |
OLD | NEW |