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 3 4"""Core control stuff for coverage.py.""" 5 6import atexit 7import inspect 8import os 9import platform 10import re 11import sys 12import traceback 13 14from coverage import env, files 15from coverage.annotate import AnnotateReporter 16from coverage.backward import string_class, iitems 17from coverage.collector import Collector 18from coverage.config import CoverageConfig 19from coverage.data import CoverageData, CoverageDataFiles 20from coverage.debug import DebugControl 21from coverage.files import TreeMatcher, FnmatchMatcher 22from coverage.files import PathAliases, find_python_files, prep_patterns 23from coverage.files import ModuleMatcher, abs_file 24from coverage.html import HtmlReporter 25from coverage.misc import CoverageException, bool_or_none, join_regex 26from coverage.misc import file_be_gone, isolate_module 27from coverage.monkey import patch_multiprocessing 28from coverage.plugin import FileReporter 29from coverage.plugin_support import Plugins 30from coverage.python import PythonFileReporter 31from coverage.results import Analysis, Numbers 32from coverage.summary import SummaryReporter 33from coverage.xmlreport import XmlReporter 34 35os = isolate_module(os) 36 37# Pypy has some unusual stuff in the "stdlib". Consider those locations 38# when deciding where the stdlib is. 39try: 40 import _structseq 41except ImportError: 42 _structseq = None 43 44 45class Coverage(object): 46 """Programmatic access to coverage.py. 47 48 To use:: 49 50 from coverage import Coverage 51 52 cov = Coverage() 53 cov.start() 54 #.. call your code .. 55 cov.stop() 56 cov.html_report(directory='covhtml') 57 58 """ 59 def __init__( 60 self, data_file=None, data_suffix=None, cover_pylib=None, 61 auto_data=False, timid=None, branch=None, config_file=True, 62 source=None, omit=None, include=None, debug=None, 63 concurrency=None, 64 ): 65 """ 66 `data_file` is the base name of the data file to use, defaulting to 67 ".coverage". `data_suffix` is appended (with a dot) to `data_file` to 68 create the final file name. If `data_suffix` is simply True, then a 69 suffix is created with the machine and process identity included. 70 71 `cover_pylib` is a boolean determining whether Python code installed 72 with the Python interpreter is measured. This includes the Python 73 standard library and any packages installed with the interpreter. 74 75 If `auto_data` is true, then any existing data file will be read when 76 coverage measurement starts, and data will be saved automatically when 77 measurement stops. 78 79 If `timid` is true, then a slower and simpler trace function will be 80 used. This is important for some environments where manipulation of 81 tracing functions breaks the faster trace function. 82 83 If `branch` is true, then branch coverage will be measured in addition 84 to the usual statement coverage. 85 86 `config_file` determines what configuration file to read: 87 88 * If it is ".coveragerc", it is interpreted as if it were True, 89 for backward compatibility. 90 91 * If it is a string, it is the name of the file to read. If the 92 file can't be read, it is an error. 93 94 * If it is True, then a few standard files names are tried 95 (".coveragerc", "setup.cfg"). It is not an error for these files 96 to not be found. 97 98 * If it is False, then no configuration file is read. 99 100 `source` is a list of file paths or package names. Only code located 101 in the trees indicated by the file paths or package names will be 102 measured. 103 104 `include` and `omit` are lists of file name patterns. Files that match 105 `include` will be measured, files that match `omit` will not. Each 106 will also accept a single string argument. 107 108 `debug` is a list of strings indicating what debugging information is 109 desired. 110 111 `concurrency` is a string indicating the concurrency library being used 112 in the measured code. Without this, coverage.py will get incorrect 113 results. Valid strings are "greenlet", "eventlet", "gevent", or 114 "thread" (the default). 115 116 .. versionadded:: 4.0 117 The `concurrency` parameter. 118 119 """ 120 # Build our configuration from a number of sources: 121 # 1: defaults: 122 self.config = CoverageConfig() 123 124 # 2: from the rcfile, .coveragerc or setup.cfg file: 125 if config_file: 126 did_read_rc = False 127 # Some API users were specifying ".coveragerc" to mean the same as 128 # True, so make it so. 129 if config_file == ".coveragerc": 130 config_file = True 131 specified_file = (config_file is not True) 132 if not specified_file: 133 config_file = ".coveragerc" 134 135 did_read_rc = self.config.from_file(config_file) 136 137 if not did_read_rc: 138 if specified_file: 139 raise CoverageException( 140 "Couldn't read '%s' as a config file" % config_file 141 ) 142 self.config.from_file("setup.cfg", section_prefix="coverage:") 143 144 # 3: from environment variables: 145 env_data_file = os.environ.get('COVERAGE_FILE') 146 if env_data_file: 147 self.config.data_file = env_data_file 148 debugs = os.environ.get('COVERAGE_DEBUG') 149 if debugs: 150 self.config.debug.extend(debugs.split(",")) 151 152 # 4: from constructor arguments: 153 self.config.from_args( 154 data_file=data_file, cover_pylib=cover_pylib, timid=timid, 155 branch=branch, parallel=bool_or_none(data_suffix), 156 source=source, omit=omit, include=include, debug=debug, 157 concurrency=concurrency, 158 ) 159 160 self._debug_file = None 161 self._auto_data = auto_data 162 self._data_suffix = data_suffix 163 164 # The matchers for _should_trace. 165 self.source_match = None 166 self.source_pkgs_match = None 167 self.pylib_match = self.cover_match = None 168 self.include_match = self.omit_match = None 169 170 # Is it ok for no data to be collected? 171 self._warn_no_data = True 172 self._warn_unimported_source = True 173 174 # A record of all the warnings that have been issued. 175 self._warnings = [] 176 177 # Other instance attributes, set later. 178 self.omit = self.include = self.source = None 179 self.source_pkgs = None 180 self.data = self.data_files = self.collector = None 181 self.plugins = None 182 self.pylib_dirs = self.cover_dirs = None 183 self.data_suffix = self.run_suffix = None 184 self._exclude_re = None 185 self.debug = None 186 187 # State machine variables: 188 # Have we initialized everything? 189 self._inited = False 190 # Have we started collecting and not stopped it? 191 self._started = False 192 # Have we measured some data and not harvested it? 193 self._measured = False 194 195 def _init(self): 196 """Set all the initial state. 197 198 This is called by the public methods to initialize state. This lets us 199 construct a :class:`Coverage` object, then tweak its state before this 200 function is called. 201 202 """ 203 if self._inited: 204 return 205 206 # Create and configure the debugging controller. COVERAGE_DEBUG_FILE 207 # is an environment variable, the name of a file to append debug logs 208 # to. 209 if self._debug_file is None: 210 debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE") 211 if debug_file_name: 212 self._debug_file = open(debug_file_name, "a") 213 else: 214 self._debug_file = sys.stderr 215 self.debug = DebugControl(self.config.debug, self._debug_file) 216 217 # Load plugins 218 self.plugins = Plugins.load_plugins(self.config.plugins, self.config, self.debug) 219 220 # _exclude_re is a dict that maps exclusion list names to compiled 221 # regexes. 222 self._exclude_re = {} 223 self._exclude_regex_stale() 224 225 files.set_relative_directory() 226 227 # The source argument can be directories or package names. 228 self.source = [] 229 self.source_pkgs = [] 230 for src in self.config.source or []: 231 if os.path.exists(src): 232 self.source.append(files.canonical_filename(src)) 233 else: 234 self.source_pkgs.append(src) 235 236 self.omit = prep_patterns(self.config.omit) 237 self.include = prep_patterns(self.config.include) 238 239 concurrency = self.config.concurrency 240 if concurrency == "multiprocessing": 241 patch_multiprocessing() 242 concurrency = None 243 244 self.collector = Collector( 245 should_trace=self._should_trace, 246 check_include=self._check_include_omit_etc, 247 timid=self.config.timid, 248 branch=self.config.branch, 249 warn=self._warn, 250 concurrency=concurrency, 251 ) 252 253 # Early warning if we aren't going to be able to support plugins. 254 if self.plugins.file_tracers and not self.collector.supports_plugins: 255 self._warn( 256 "Plugin file tracers (%s) aren't supported with %s" % ( 257 ", ".join( 258 plugin._coverage_plugin_name 259 for plugin in self.plugins.file_tracers 260 ), 261 self.collector.tracer_name(), 262 ) 263 ) 264 for plugin in self.plugins.file_tracers: 265 plugin._coverage_enabled = False 266 267 # Suffixes are a bit tricky. We want to use the data suffix only when 268 # collecting data, not when combining data. So we save it as 269 # `self.run_suffix` now, and promote it to `self.data_suffix` if we 270 # find that we are collecting data later. 271 if self._data_suffix or self.config.parallel: 272 if not isinstance(self._data_suffix, string_class): 273 # if data_suffix=True, use .machinename.pid.random 274 self._data_suffix = True 275 else: 276 self._data_suffix = None 277 self.data_suffix = None 278 self.run_suffix = self._data_suffix 279 280 # Create the data file. We do this at construction time so that the 281 # data file will be written into the directory where the process 282 # started rather than wherever the process eventually chdir'd to. 283 self.data = CoverageData(debug=self.debug) 284 self.data_files = CoverageDataFiles(basename=self.config.data_file, warn=self._warn) 285 286 # The directories for files considered "installed with the interpreter". 287 self.pylib_dirs = set() 288 if not self.config.cover_pylib: 289 # Look at where some standard modules are located. That's the 290 # indication for "installed with the interpreter". In some 291 # environments (virtualenv, for example), these modules may be 292 # spread across a few locations. Look at all the candidate modules 293 # we've imported, and take all the different ones. 294 for m in (atexit, inspect, os, platform, re, _structseq, traceback): 295 if m is not None and hasattr(m, "__file__"): 296 self.pylib_dirs.add(self._canonical_dir(m)) 297 if _structseq and not hasattr(_structseq, '__file__'): 298 # PyPy 2.4 has no __file__ in the builtin modules, but the code 299 # objects still have the file names. So dig into one to find 300 # the path to exclude. 301 structseq_new = _structseq.structseq_new 302 try: 303 structseq_file = structseq_new.func_code.co_filename 304 except AttributeError: 305 structseq_file = structseq_new.__code__.co_filename 306 self.pylib_dirs.add(self._canonical_dir(structseq_file)) 307 308 # To avoid tracing the coverage.py code itself, we skip anything 309 # located where we are. 310 self.cover_dirs = [self._canonical_dir(__file__)] 311 if env.TESTING: 312 # When testing, we use PyContracts, which should be considered 313 # part of coverage.py, and it uses six. Exclude those directories 314 # just as we exclude ourselves. 315 import contracts, six 316 for mod in [contracts, six]: 317 self.cover_dirs.append(self._canonical_dir(mod)) 318 319 # Set the reporting precision. 320 Numbers.set_precision(self.config.precision) 321 322 atexit.register(self._atexit) 323 324 self._inited = True 325 326 # Create the matchers we need for _should_trace 327 if self.source or self.source_pkgs: 328 self.source_match = TreeMatcher(self.source) 329 self.source_pkgs_match = ModuleMatcher(self.source_pkgs) 330 else: 331 if self.cover_dirs: 332 self.cover_match = TreeMatcher(self.cover_dirs) 333 if self.pylib_dirs: 334 self.pylib_match = TreeMatcher(self.pylib_dirs) 335 if self.include: 336 self.include_match = FnmatchMatcher(self.include) 337 if self.omit: 338 self.omit_match = FnmatchMatcher(self.omit) 339 340 # The user may want to debug things, show info if desired. 341 wrote_any = False 342 if self.debug.should('config'): 343 config_info = sorted(self.config.__dict__.items()) 344 self.debug.write_formatted_info("config", config_info) 345 wrote_any = True 346 347 if self.debug.should('sys'): 348 self.debug.write_formatted_info("sys", self.sys_info()) 349 for plugin in self.plugins: 350 header = "sys: " + plugin._coverage_plugin_name 351 info = plugin.sys_info() 352 self.debug.write_formatted_info(header, info) 353 wrote_any = True 354 355 if wrote_any: 356 self.debug.write_formatted_info("end", ()) 357 358 def _canonical_dir(self, morf): 359 """Return the canonical directory of the module or file `morf`.""" 360 morf_filename = PythonFileReporter(morf, self).filename 361 return os.path.split(morf_filename)[0] 362 363 def _source_for_file(self, filename): 364 """Return the source file for `filename`. 365 366 Given a file name being traced, return the best guess as to the source 367 file to attribute it to. 368 369 """ 370 if filename.endswith(".py"): 371 # .py files are themselves source files. 372 return filename 373 374 elif filename.endswith((".pyc", ".pyo")): 375 # Bytecode files probably have source files near them. 376 py_filename = filename[:-1] 377 if os.path.exists(py_filename): 378 # Found a .py file, use that. 379 return py_filename 380 if env.WINDOWS: 381 # On Windows, it could be a .pyw file. 382 pyw_filename = py_filename + "w" 383 if os.path.exists(pyw_filename): 384 return pyw_filename 385 # Didn't find source, but it's probably the .py file we want. 386 return py_filename 387 388 elif filename.endswith("$py.class"): 389 # Jython is easy to guess. 390 return filename[:-9] + ".py" 391 392 # No idea, just use the file name as-is. 393 return filename 394 395 def _name_for_module(self, module_globals, filename): 396 """Get the name of the module for a set of globals and file name. 397 398 For configurability's sake, we allow __main__ modules to be matched by 399 their importable name. 400 401 If loaded via runpy (aka -m), we can usually recover the "original" 402 full dotted module name, otherwise, we resort to interpreting the 403 file name to get the module's name. In the case that the module name 404 can't be determined, None is returned. 405 406 """ 407 dunder_name = module_globals.get('__name__', None) 408 409 if isinstance(dunder_name, str) and dunder_name != '__main__': 410 # This is the usual case: an imported module. 411 return dunder_name 412 413 loader = module_globals.get('__loader__', None) 414 for attrname in ('fullname', 'name'): # attribute renamed in py3.2 415 if hasattr(loader, attrname): 416 fullname = getattr(loader, attrname) 417 else: 418 continue 419 420 if isinstance(fullname, str) and fullname != '__main__': 421 # Module loaded via: runpy -m 422 return fullname 423 424 # Script as first argument to Python command line. 425 inspectedname = inspect.getmodulename(filename) 426 if inspectedname is not None: 427 return inspectedname 428 else: 429 return dunder_name 430 431 def _should_trace_internal(self, filename, frame): 432 """Decide whether to trace execution in `filename`, with a reason. 433 434 This function is called from the trace function. As each new file name 435 is encountered, this function determines whether it is traced or not. 436 437 Returns a FileDisposition object. 438 439 """ 440 original_filename = filename 441 disp = _disposition_init(self.collector.file_disposition_class, filename) 442 443 def nope(disp, reason): 444 """Simple helper to make it easy to return NO.""" 445 disp.trace = False 446 disp.reason = reason 447 return disp 448 449 # Compiled Python files have two file names: frame.f_code.co_filename is 450 # the file name at the time the .pyc was compiled. The second name is 451 # __file__, which is where the .pyc was actually loaded from. Since 452 # .pyc files can be moved after compilation (for example, by being 453 # installed), we look for __file__ in the frame and prefer it to the 454 # co_filename value. 455 dunder_file = frame.f_globals.get('__file__') 456 if dunder_file: 457 filename = self._source_for_file(dunder_file) 458 if original_filename and not original_filename.startswith('<'): 459 orig = os.path.basename(original_filename) 460 if orig != os.path.basename(filename): 461 # Files shouldn't be renamed when moved. This happens when 462 # exec'ing code. If it seems like something is wrong with 463 # the frame's file name, then just use the original. 464 filename = original_filename 465 466 if not filename: 467 # Empty string is pretty useless. 468 return nope(disp, "empty string isn't a file name") 469 470 if filename.startswith('memory:'): 471 return nope(disp, "memory isn't traceable") 472 473 if filename.startswith('<'): 474 # Lots of non-file execution is represented with artificial 475 # file names like "<string>", "<doctest readme.txt[0]>", or 476 # "<exec_function>". Don't ever trace these executions, since we 477 # can't do anything with the data later anyway. 478 return nope(disp, "not a real file name") 479 480 # pyexpat does a dumb thing, calling the trace function explicitly from 481 # C code with a C file name. 482 if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename): 483 return nope(disp, "pyexpat lies about itself") 484 485 # Jython reports the .class file to the tracer, use the source file. 486 if filename.endswith("$py.class"): 487 filename = filename[:-9] + ".py" 488 489 canonical = files.canonical_filename(filename) 490 disp.canonical_filename = canonical 491 492 # Try the plugins, see if they have an opinion about the file. 493 plugin = None 494 for plugin in self.plugins.file_tracers: 495 if not plugin._coverage_enabled: 496 continue 497 498 try: 499 file_tracer = plugin.file_tracer(canonical) 500 if file_tracer is not None: 501 file_tracer._coverage_plugin = plugin 502 disp.trace = True 503 disp.file_tracer = file_tracer 504 if file_tracer.has_dynamic_source_filename(): 505 disp.has_dynamic_filename = True 506 else: 507 disp.source_filename = files.canonical_filename( 508 file_tracer.source_filename() 509 ) 510 break 511 except Exception: 512 self._warn( 513 "Disabling plugin %r due to an exception:" % ( 514 plugin._coverage_plugin_name 515 ) 516 ) 517 traceback.print_exc() 518 plugin._coverage_enabled = False 519 continue 520 else: 521 # No plugin wanted it: it's Python. 522 disp.trace = True 523 disp.source_filename = canonical 524 525 if not disp.has_dynamic_filename: 526 if not disp.source_filename: 527 raise CoverageException( 528 "Plugin %r didn't set source_filename for %r" % 529 (plugin, disp.original_filename) 530 ) 531 reason = self._check_include_omit_etc_internal( 532 disp.source_filename, frame, 533 ) 534 if reason: 535 nope(disp, reason) 536 537 return disp 538 539 def _check_include_omit_etc_internal(self, filename, frame): 540 """Check a file name against the include, omit, etc, rules. 541 542 Returns a string or None. String means, don't trace, and is the reason 543 why. None means no reason found to not trace. 544 545 """ 546 modulename = self._name_for_module(frame.f_globals, filename) 547 548 # If the user specified source or include, then that's authoritative 549 # about the outer bound of what to measure and we don't have to apply 550 # any canned exclusions. If they didn't, then we have to exclude the 551 # stdlib and coverage.py directories. 552 if self.source_match: 553 if self.source_pkgs_match.match(modulename): 554 if modulename in self.source_pkgs: 555 self.source_pkgs.remove(modulename) 556 return None # There's no reason to skip this file. 557 558 if not self.source_match.match(filename): 559 return "falls outside the --source trees" 560 elif self.include_match: 561 if not self.include_match.match(filename): 562 return "falls outside the --include trees" 563 else: 564 # If we aren't supposed to trace installed code, then check if this 565 # is near the Python standard library and skip it if so. 566 if self.pylib_match and self.pylib_match.match(filename): 567 return "is in the stdlib" 568 569 # We exclude the coverage.py code itself, since a little of it 570 # will be measured otherwise. 571 if self.cover_match and self.cover_match.match(filename): 572 return "is part of coverage.py" 573 574 # Check the file against the omit pattern. 575 if self.omit_match and self.omit_match.match(filename): 576 return "is inside an --omit pattern" 577 578 # No reason found to skip this file. 579 return None 580 581 def _should_trace(self, filename, frame): 582 """Decide whether to trace execution in `filename`. 583 584 Calls `_should_trace_internal`, and returns the FileDisposition. 585 586 """ 587 disp = self._should_trace_internal(filename, frame) 588 if self.debug.should('trace'): 589 self.debug.write(_disposition_debug_msg(disp)) 590 return disp 591 592 def _check_include_omit_etc(self, filename, frame): 593 """Check a file name against the include/omit/etc, rules, verbosely. 594 595 Returns a boolean: True if the file should be traced, False if not. 596 597 """ 598 reason = self._check_include_omit_etc_internal(filename, frame) 599 if self.debug.should('trace'): 600 if not reason: 601 msg = "Including %r" % (filename,) 602 else: 603 msg = "Not including %r: %s" % (filename, reason) 604 self.debug.write(msg) 605 606 return not reason 607 608 def _warn(self, msg): 609 """Use `msg` as a warning.""" 610 self._warnings.append(msg) 611 if self.debug.should('pid'): 612 msg = "[%d] %s" % (os.getpid(), msg) 613 sys.stderr.write("Coverage.py warning: %s\n" % msg) 614 615 def get_option(self, option_name): 616 """Get an option from the configuration. 617 618 `option_name` is a colon-separated string indicating the section and 619 option name. For example, the ``branch`` option in the ``[run]`` 620 section of the config file would be indicated with `"run:branch"`. 621 622 Returns the value of the option. 623 624 .. versionadded:: 4.0 625 626 """ 627 return self.config.get_option(option_name) 628 629 def set_option(self, option_name, value): 630 """Set an option in the configuration. 631 632 `option_name` is a colon-separated string indicating the section and 633 option name. For example, the ``branch`` option in the ``[run]`` 634 section of the config file would be indicated with ``"run:branch"``. 635 636 `value` is the new value for the option. This should be a Python 637 value where appropriate. For example, use True for booleans, not the 638 string ``"True"``. 639 640 As an example, calling:: 641 642 cov.set_option("run:branch", True) 643 644 has the same effect as this configuration file:: 645 646 [run] 647 branch = True 648 649 .. versionadded:: 4.0 650 651 """ 652 self.config.set_option(option_name, value) 653 654 def use_cache(self, usecache): 655 """Obsolete method.""" 656 self._init() 657 if not usecache: 658 self._warn("use_cache(False) is no longer supported.") 659 660 def load(self): 661 """Load previously-collected coverage data from the data file.""" 662 self._init() 663 self.collector.reset() 664 self.data_files.read(self.data) 665 666 def start(self): 667 """Start measuring code coverage. 668 669 Coverage measurement actually occurs in functions called after 670 :meth:`start` is invoked. Statements in the same scope as 671 :meth:`start` won't be measured. 672 673 Once you invoke :meth:`start`, you must also call :meth:`stop` 674 eventually, or your process might not shut down cleanly. 675 676 """ 677 self._init() 678 if self.run_suffix: 679 # Calling start() means we're running code, so use the run_suffix 680 # as the data_suffix when we eventually save the data. 681 self.data_suffix = self.run_suffix 682 if self._auto_data: 683 self.load() 684 685 self.collector.start() 686 self._started = True 687 self._measured = True 688 689 def stop(self): 690 """Stop measuring code coverage.""" 691 if self._started: 692 self.collector.stop() 693 self._started = False 694 695 def _atexit(self): 696 """Clean up on process shutdown.""" 697 if self._started: 698 self.stop() 699 if self._auto_data: 700 self.save() 701 702 def erase(self): 703 """Erase previously-collected coverage data. 704 705 This removes the in-memory data collected in this session as well as 706 discarding the data file. 707 708 """ 709 self._init() 710 self.collector.reset() 711 self.data.erase() 712 self.data_files.erase(parallel=self.config.parallel) 713 714 def clear_exclude(self, which='exclude'): 715 """Clear the exclude list.""" 716 self._init() 717 setattr(self.config, which + "_list", []) 718 self._exclude_regex_stale() 719 720 def exclude(self, regex, which='exclude'): 721 """Exclude source lines from execution consideration. 722 723 A number of lists of regular expressions are maintained. Each list 724 selects lines that are treated differently during reporting. 725 726 `which` determines which list is modified. The "exclude" list selects 727 lines that are not considered executable at all. The "partial" list 728 indicates lines with branches that are not taken. 729 730 `regex` is a regular expression. The regex is added to the specified 731 list. If any of the regexes in the list is found in a line, the line 732 is marked for special treatment during reporting. 733 734 """ 735 self._init() 736 excl_list = getattr(self.config, which + "_list") 737 excl_list.append(regex) 738 self._exclude_regex_stale() 739 740 def _exclude_regex_stale(self): 741 """Drop all the compiled exclusion regexes, a list was modified.""" 742 self._exclude_re.clear() 743 744 def _exclude_regex(self, which): 745 """Return a compiled regex for the given exclusion list.""" 746 if which not in self._exclude_re: 747 excl_list = getattr(self.config, which + "_list") 748 self._exclude_re[which] = join_regex(excl_list) 749 return self._exclude_re[which] 750 751 def get_exclude_list(self, which='exclude'): 752 """Return a list of excluded regex patterns. 753 754 `which` indicates which list is desired. See :meth:`exclude` for the 755 lists that are available, and their meaning. 756 757 """ 758 self._init() 759 return getattr(self.config, which + "_list") 760 761 def save(self): 762 """Save the collected coverage data to the data file.""" 763 self._init() 764 self.get_data() 765 self.data_files.write(self.data, suffix=self.data_suffix) 766 767 def combine(self, data_paths=None): 768 """Combine together a number of similarly-named coverage data files. 769 770 All coverage data files whose name starts with `data_file` (from the 771 coverage() constructor) will be read, and combined together into the 772 current measurements. 773 774 `data_paths` is a list of files or directories from which data should 775 be combined. If no list is passed, then the data files from the 776 directory indicated by the current data file (probably the current 777 directory) will be combined. 778 779 .. versionadded:: 4.0 780 The `data_paths` parameter. 781 782 """ 783 self._init() 784 self.get_data() 785 786 aliases = None 787 if self.config.paths: 788 aliases = PathAliases() 789 for paths in self.config.paths.values(): 790 result = paths[0] 791 for pattern in paths[1:]: 792 aliases.add(pattern, result) 793 794 self.data_files.combine_parallel_data(self.data, aliases=aliases, data_paths=data_paths) 795 796 def get_data(self): 797 """Get the collected data and reset the collector. 798 799 Also warn about various problems collecting data. 800 801 Returns a :class:`coverage.CoverageData`, the collected coverage data. 802 803 .. versionadded:: 4.0 804 805 """ 806 self._init() 807 if not self._measured: 808 return self.data 809 810 self.collector.save_data(self.data) 811 812 # If there are still entries in the source_pkgs list, then we never 813 # encountered those packages. 814 if self._warn_unimported_source: 815 for pkg in self.source_pkgs: 816 if pkg not in sys.modules: 817 self._warn("Module %s was never imported." % pkg) 818 elif not ( 819 hasattr(sys.modules[pkg], '__file__') and 820 os.path.exists(sys.modules[pkg].__file__) 821 ): 822 self._warn("Module %s has no Python source." % pkg) 823 else: 824 self._warn("Module %s was previously imported, but not measured." % pkg) 825 826 # Find out if we got any data. 827 if not self.data and self._warn_no_data: 828 self._warn("No data was collected.") 829 830 # Find files that were never executed at all. 831 for src in self.source: 832 for py_file in find_python_files(src): 833 py_file = files.canonical_filename(py_file) 834 835 if self.omit_match and self.omit_match.match(py_file): 836 # Turns out this file was omitted, so don't pull it back 837 # in as unexecuted. 838 continue 839 840 self.data.touch_file(py_file) 841 842 if self.config.note: 843 self.data.add_run_info(note=self.config.note) 844 845 self._measured = False 846 return self.data 847 848 # Backward compatibility with version 1. 849 def analysis(self, morf): 850 """Like `analysis2` but doesn't return excluded line numbers.""" 851 f, s, _, m, mf = self.analysis2(morf) 852 return f, s, m, mf 853 854 def analysis2(self, morf): 855 """Analyze a module. 856 857 `morf` is a module or a file name. It will be analyzed to determine 858 its coverage statistics. The return value is a 5-tuple: 859 860 * The file name for the module. 861 * A list of line numbers of executable statements. 862 * A list of line numbers of excluded statements. 863 * A list of line numbers of statements not run (missing from 864 execution). 865 * A readable formatted string of the missing line numbers. 866 867 The analysis uses the source file itself and the current measured 868 coverage data. 869 870 """ 871 self._init() 872 analysis = self._analyze(morf) 873 return ( 874 analysis.filename, 875 sorted(analysis.statements), 876 sorted(analysis.excluded), 877 sorted(analysis.missing), 878 analysis.missing_formatted(), 879 ) 880 881 def _analyze(self, it): 882 """Analyze a single morf or code unit. 883 884 Returns an `Analysis` object. 885 886 """ 887 self.get_data() 888 if not isinstance(it, FileReporter): 889 it = self._get_file_reporter(it) 890 891 return Analysis(self.data, it) 892 893 def _get_file_reporter(self, morf): 894 """Get a FileReporter for a module or file name.""" 895 plugin = None 896 file_reporter = "python" 897 898 if isinstance(morf, string_class): 899 abs_morf = abs_file(morf) 900 plugin_name = self.data.file_tracer(abs_morf) 901 if plugin_name: 902 plugin = self.plugins.get(plugin_name) 903 904 if plugin: 905 file_reporter = plugin.file_reporter(abs_morf) 906 if file_reporter is None: 907 raise CoverageException( 908 "Plugin %r did not provide a file reporter for %r." % ( 909 plugin._coverage_plugin_name, morf 910 ) 911 ) 912 913 if file_reporter == "python": 914 file_reporter = PythonFileReporter(morf, self) 915 916 return file_reporter 917 918 def _get_file_reporters(self, morfs=None): 919 """Get a list of FileReporters for a list of modules or file names. 920 921 For each module or file name in `morfs`, find a FileReporter. Return 922 the list of FileReporters. 923 924 If `morfs` is a single module or file name, this returns a list of one 925 FileReporter. If `morfs` is empty or None, then the list of all files 926 measured is used to find the FileReporters. 927 928 """ 929 if not morfs: 930 morfs = self.data.measured_files() 931 932 # Be sure we have a list. 933 if not isinstance(morfs, (list, tuple)): 934 morfs = [morfs] 935 936 file_reporters = [] 937 for morf in morfs: 938 file_reporter = self._get_file_reporter(morf) 939 file_reporters.append(file_reporter) 940 941 return file_reporters 942 943 def report( 944 self, morfs=None, show_missing=True, ignore_errors=None, 945 file=None, # pylint: disable=redefined-builtin 946 omit=None, include=None, skip_covered=False, 947 ): 948 """Write a summary report to `file`. 949 950 Each module in `morfs` is listed, with counts of statements, executed 951 statements, missing statements, and a list of lines missed. 952 953 `include` is a list of file name patterns. Files that match will be 954 included in the report. Files matching `omit` will not be included in 955 the report. 956 957 Returns a float, the total percentage covered. 958 959 """ 960 self.get_data() 961 self.config.from_args( 962 ignore_errors=ignore_errors, omit=omit, include=include, 963 show_missing=show_missing, skip_covered=skip_covered, 964 ) 965 reporter = SummaryReporter(self, self.config) 966 return reporter.report(morfs, outfile=file) 967 968 def annotate( 969 self, morfs=None, directory=None, ignore_errors=None, 970 omit=None, include=None, 971 ): 972 """Annotate a list of modules. 973 974 Each module in `morfs` is annotated. The source is written to a new 975 file, named with a ",cover" suffix, with each line prefixed with a 976 marker to indicate the coverage of the line. Covered lines have ">", 977 excluded lines have "-", and missing lines have "!". 978 979 See :meth:`report` for other arguments. 980 981 """ 982 self.get_data() 983 self.config.from_args( 984 ignore_errors=ignore_errors, omit=omit, include=include 985 ) 986 reporter = AnnotateReporter(self, self.config) 987 reporter.report(morfs, directory=directory) 988 989 def html_report(self, morfs=None, directory=None, ignore_errors=None, 990 omit=None, include=None, extra_css=None, title=None): 991 """Generate an HTML report. 992 993 The HTML is written to `directory`. The file "index.html" is the 994 overview starting point, with links to more detailed pages for 995 individual modules. 996 997 `extra_css` is a path to a file of other CSS to apply on the page. 998 It will be copied into the HTML directory. 999 1000 `title` is a text string (not HTML) to use as the title of the HTML 1001 report. 1002 1003 See :meth:`report` for other arguments. 1004 1005 Returns a float, the total percentage covered. 1006 1007 """ 1008 self.get_data() 1009 self.config.from_args( 1010 ignore_errors=ignore_errors, omit=omit, include=include, 1011 html_dir=directory, extra_css=extra_css, html_title=title, 1012 ) 1013 reporter = HtmlReporter(self, self.config) 1014 return reporter.report(morfs) 1015 1016 def xml_report( 1017 self, morfs=None, outfile=None, ignore_errors=None, 1018 omit=None, include=None, 1019 ): 1020 """Generate an XML report of coverage results. 1021 1022 The report is compatible with Cobertura reports. 1023 1024 Each module in `morfs` is included in the report. `outfile` is the 1025 path to write the file to, "-" will write to stdout. 1026 1027 See :meth:`report` for other arguments. 1028 1029 Returns a float, the total percentage covered. 1030 1031 """ 1032 self.get_data() 1033 self.config.from_args( 1034 ignore_errors=ignore_errors, omit=omit, include=include, 1035 xml_output=outfile, 1036 ) 1037 file_to_close = None 1038 delete_file = False 1039 if self.config.xml_output: 1040 if self.config.xml_output == '-': 1041 outfile = sys.stdout 1042 else: 1043 # Ensure that the output directory is created; done here 1044 # because this report pre-opens the output file. 1045 # HTMLReport does this using the Report plumbing because 1046 # its task is more complex, being multiple files. 1047 output_dir = os.path.dirname(self.config.xml_output) 1048 if output_dir and not os.path.isdir(output_dir): 1049 os.makedirs(output_dir) 1050 open_kwargs = {} 1051 if env.PY3: 1052 open_kwargs['encoding'] = 'utf8' 1053 outfile = open(self.config.xml_output, "w", **open_kwargs) 1054 file_to_close = outfile 1055 try: 1056 reporter = XmlReporter(self, self.config) 1057 return reporter.report(morfs, outfile=outfile) 1058 except CoverageException: 1059 delete_file = True 1060 raise 1061 finally: 1062 if file_to_close: 1063 file_to_close.close() 1064 if delete_file: 1065 file_be_gone(self.config.xml_output) 1066 1067 def sys_info(self): 1068 """Return a list of (key, value) pairs showing internal information.""" 1069 1070 import coverage as covmod 1071 1072 self._init() 1073 1074 ft_plugins = [] 1075 for ft in self.plugins.file_tracers: 1076 ft_name = ft._coverage_plugin_name 1077 if not ft._coverage_enabled: 1078 ft_name += " (disabled)" 1079 ft_plugins.append(ft_name) 1080 1081 info = [ 1082 ('version', covmod.__version__), 1083 ('coverage', covmod.__file__), 1084 ('cover_dirs', self.cover_dirs), 1085 ('pylib_dirs', self.pylib_dirs), 1086 ('tracer', self.collector.tracer_name()), 1087 ('plugins.file_tracers', ft_plugins), 1088 ('config_files', self.config.attempted_config_files), 1089 ('configs_read', self.config.config_files), 1090 ('data_path', self.data_files.filename), 1091 ('python', sys.version.replace('\n', '')), 1092 ('platform', platform.platform()), 1093 ('implementation', platform.python_implementation()), 1094 ('executable', sys.executable), 1095 ('cwd', os.getcwd()), 1096 ('path', sys.path), 1097 ('environment', sorted( 1098 ("%s = %s" % (k, v)) 1099 for k, v in iitems(os.environ) 1100 if k.startswith(("COV", "PY")) 1101 )), 1102 ('command_line', " ".join(getattr(sys, 'argv', ['???']))), 1103 ] 1104 1105 matcher_names = [ 1106 'source_match', 'source_pkgs_match', 1107 'include_match', 'omit_match', 1108 'cover_match', 'pylib_match', 1109 ] 1110 1111 for matcher_name in matcher_names: 1112 matcher = getattr(self, matcher_name) 1113 if matcher: 1114 matcher_info = matcher.info() 1115 else: 1116 matcher_info = '-none-' 1117 info.append((matcher_name, matcher_info)) 1118 1119 return info 1120 1121 1122# FileDisposition "methods": FileDisposition is a pure value object, so it can 1123# be implemented in either C or Python. Acting on them is done with these 1124# functions. 1125 1126def _disposition_init(cls, original_filename): 1127 """Construct and initialize a new FileDisposition object.""" 1128 disp = cls() 1129 disp.original_filename = original_filename 1130 disp.canonical_filename = original_filename 1131 disp.source_filename = None 1132 disp.trace = False 1133 disp.reason = "" 1134 disp.file_tracer = None 1135 disp.has_dynamic_filename = False 1136 return disp 1137 1138 1139def _disposition_debug_msg(disp): 1140 """Make a nice debug message of what the FileDisposition is doing.""" 1141 if disp.trace: 1142 msg = "Tracing %r" % (disp.original_filename,) 1143 if disp.file_tracer: 1144 msg += ": will be traced by %r" % disp.file_tracer 1145 else: 1146 msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason) 1147 return msg 1148 1149 1150def process_startup(): 1151 """Call this at Python start-up to perhaps measure coverage. 1152 1153 If the environment variable COVERAGE_PROCESS_START is defined, coverage 1154 measurement is started. The value of the variable is the config file 1155 to use. 1156 1157 There are two ways to configure your Python installation to invoke this 1158 function when Python starts: 1159 1160 #. Create or append to sitecustomize.py to add these lines:: 1161 1162 import coverage 1163 coverage.process_startup() 1164 1165 #. Create a .pth file in your Python installation containing:: 1166 1167 import coverage; coverage.process_startup() 1168 1169 """ 1170 cps = os.environ.get("COVERAGE_PROCESS_START") 1171 if not cps: 1172 # No request for coverage, nothing to do. 1173 return 1174 1175 # This function can be called more than once in a process. This happens 1176 # because some virtualenv configurations make the same directory visible 1177 # twice in sys.path. This means that the .pth file will be found twice, 1178 # and executed twice, executing this function twice. We set a global 1179 # flag (an attribute on this function) to indicate that coverage.py has 1180 # already been started, so we can avoid doing it twice. 1181 # 1182 # https://bitbucket.org/ned/coveragepy/issue/340/keyerror-subpy has more 1183 # details. 1184 1185 if hasattr(process_startup, "done"): 1186 # We've annotated this function before, so we must have already 1187 # started coverage.py in this process. Nothing to do. 1188 return 1189 1190 process_startup.done = True 1191 cov = Coverage(config_file=cps, auto_data=True) 1192 cov.start() 1193 cov._warn_no_data = False 1194 cov._warn_unimported_source = False 1195