| 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 |