Finish refactoring of DomCodeToUsLayoutKeyboardCode().
[chromium-blink-merge.git] / third_party / pycoverage / coverage / control.py
blobf75a3dda5b1b479a45e52c50d1390b7d076a8b1f
1 """Core control stuff for Coverage."""
3 import atexit, os, random, socket, sys
5 from coverage.annotate import AnnotateReporter
6 from coverage.backward import string_class, iitems, sorted # pylint: disable=W0622
7 from coverage.codeunit import code_unit_factory, CodeUnit
8 from coverage.collector import Collector
9 from coverage.config import CoverageConfig
10 from coverage.data import CoverageData
11 from coverage.debug import DebugControl
12 from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
13 from coverage.files import PathAliases, find_python_files, prep_patterns
14 from coverage.html import HtmlReporter
15 from coverage.misc import CoverageException, bool_or_none, join_regex
16 from coverage.misc import file_be_gone
17 from coverage.results import Analysis, Numbers
18 from coverage.summary import SummaryReporter
19 from coverage.xmlreport import XmlReporter
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
29 class coverage(object):
30 """Programmatic access to coverage.py.
32 To use::
34 from coverage import coverage
36 cov = coverage()
37 cov.start()
38 #.. call your code ..
39 cov.stop()
40 cov.html_report(directory='covhtml')
42 """
43 def __init__(self, data_file=None, data_suffix=None, cover_pylib=None,
44 auto_data=False, timid=None, branch=None, config_file=True,
45 source=None, omit=None, include=None, debug=None,
46 debug_file=None):
47 """
48 `data_file` is the base name of the data file to use, defaulting to
49 ".coverage". `data_suffix` is appended (with a dot) to `data_file` to
50 create the final file name. If `data_suffix` is simply True, then a
51 suffix is created with the machine and process identity included.
53 `cover_pylib` is a boolean determining whether Python code installed
54 with the Python interpreter is measured. This includes the Python
55 standard library and any packages installed with the interpreter.
57 If `auto_data` is true, then any existing data file will be read when
58 coverage measurement starts, and data will be saved automatically when
59 measurement stops.
61 If `timid` is true, then a slower and simpler trace function will be
62 used. This is important for some environments where manipulation of
63 tracing functions breaks the faster trace function.
65 If `branch` is true, then branch coverage will be measured in addition
66 to the usual statement coverage.
68 `config_file` determines what config file to read. If it is a string,
69 it is the name of the config file to read. If it is True, then a
70 standard file is read (".coveragerc"). If it is False, then no file is
71 read.
73 `source` is a list of file paths or package names. Only code located
74 in the trees indicated by the file paths or package names will be
75 measured.
77 `include` and `omit` are lists of filename patterns. Files that match
78 `include` will be measured, files that match `omit` will not. Each
79 will also accept a single string argument.
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.
85 """
86 from coverage import __version__
88 # A record of all the warnings that have been issued.
89 self._warnings = []
91 # Build our configuration from a number of sources:
92 # 1: defaults:
93 self.config = CoverageConfig()
95 # 2: from the coveragerc file:
96 if config_file:
97 if config_file is True:
98 config_file = ".coveragerc"
99 try:
100 self.config.from_file(config_file)
101 except ValueError:
102 _, err, _ = sys.exc_info()
103 raise CoverageException(
104 "Couldn't read config file %s: %s" % (config_file, err)
107 # 3: from environment variables:
108 self.config.from_environment('COVERAGE_OPTIONS')
109 env_data_file = os.environ.get('COVERAGE_FILE')
110 if env_data_file:
111 self.config.data_file = env_data_file
113 # 4: from constructor arguments:
114 self.config.from_args(
115 data_file=data_file, cover_pylib=cover_pylib, timid=timid,
116 branch=branch, parallel=bool_or_none(data_suffix),
117 source=source, omit=omit, include=include, debug=debug,
120 # Create and configure the debugging controller.
121 self.debug = DebugControl(self.config.debug, debug_file or sys.stderr)
123 self.auto_data = auto_data
125 # _exclude_re is a dict mapping exclusion list names to compiled
126 # regexes.
127 self._exclude_re = {}
128 self._exclude_regex_stale()
130 self.file_locator = FileLocator()
132 # The source argument can be directories or package names.
133 self.source = []
134 self.source_pkgs = []
135 for src in self.config.source or []:
136 if os.path.exists(src):
137 self.source.append(self.file_locator.canonical_filename(src))
138 else:
139 self.source_pkgs.append(src)
141 self.omit = prep_patterns(self.config.omit)
142 self.include = prep_patterns(self.config.include)
144 self.collector = Collector(
145 self._should_trace, timid=self.config.timid,
146 branch=self.config.branch, warn=self._warn
149 # Suffixes are a bit tricky. We want to use the data suffix only when
150 # collecting data, not when combining data. So we save it as
151 # `self.run_suffix` now, and promote it to `self.data_suffix` if we
152 # find that we are collecting data later.
153 if data_suffix or self.config.parallel:
154 if not isinstance(data_suffix, string_class):
155 # if data_suffix=True, use .machinename.pid.random
156 data_suffix = True
157 else:
158 data_suffix = None
159 self.data_suffix = None
160 self.run_suffix = data_suffix
162 # Create the data file. We do this at construction time so that the
163 # data file will be written into the directory where the process
164 # started rather than wherever the process eventually chdir'd to.
165 self.data = CoverageData(
166 basename=self.config.data_file,
167 collector="coverage v%s" % __version__,
168 debug=self.debug,
171 # The dirs for files considered "installed with the interpreter".
172 self.pylib_dirs = []
173 if not self.config.cover_pylib:
174 # Look at where some standard modules are located. That's the
175 # indication for "installed with the interpreter". In some
176 # environments (virtualenv, for example), these modules may be
177 # spread across a few locations. Look at all the candidate modules
178 # we've imported, and take all the different ones.
179 for m in (atexit, os, random, socket, _structseq):
180 if m is not None and hasattr(m, "__file__"):
181 m_dir = self._canonical_dir(m)
182 if m_dir not in self.pylib_dirs:
183 self.pylib_dirs.append(m_dir)
185 # To avoid tracing the coverage code itself, we skip anything located
186 # where we are.
187 self.cover_dir = self._canonical_dir(__file__)
189 # The matchers for _should_trace.
190 self.source_match = None
191 self.pylib_match = self.cover_match = None
192 self.include_match = self.omit_match = None
194 # Set the reporting precision.
195 Numbers.set_precision(self.config.precision)
197 # Is it ok for no data to be collected?
198 self._warn_no_data = True
199 self._warn_unimported_source = True
201 # State machine variables:
202 # Have we started collecting and not stopped it?
203 self._started = False
204 # Have we measured some data and not harvested it?
205 self._measured = False
207 atexit.register(self._atexit)
209 def _canonical_dir(self, morf):
210 """Return the canonical directory of the module or file `morf`."""
211 return os.path.split(CodeUnit(morf, self.file_locator).filename)[0]
213 def _source_for_file(self, filename):
214 """Return the source file for `filename`."""
215 if not filename.endswith(".py"):
216 if filename[-4:-1] == ".py":
217 filename = filename[:-1]
218 elif filename.endswith("$py.class"): # jython
219 filename = filename[:-9] + ".py"
220 return filename
222 def _should_trace_with_reason(self, filename, frame):
223 """Decide whether to trace execution in `filename`, with a reason.
225 This function is called from the trace function. As each new file name
226 is encountered, this function determines whether it is traced or not.
228 Returns a pair of values: the first indicates whether the file should
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.
234 if not filename:
235 # Empty string is pretty useless
236 return None, "empty string isn't a filename"
238 if filename.startswith('<'):
239 # Lots of non-file execution is represented with artificial
240 # filenames like "<string>", "<doctest readme.txt[0]>", or
241 # "<exec_function>". Don't ever trace these executions, since we
242 # can't do anything with the data later anyway.
243 return None, "not a real filename"
245 self._check_for_packages()
247 # Compiled Python files have two filenames: frame.f_code.co_filename is
248 # the filename at the time the .pyc was compiled. The second name is
249 # __file__, which is where the .pyc was actually loaded from. Since
250 # .pyc files can be moved after compilation (for example, by being
251 # installed), we look for __file__ in the frame and prefer it to the
252 # co_filename value.
253 dunder_file = frame.f_globals.get('__file__')
254 if dunder_file:
255 filename = self._source_for_file(dunder_file)
257 # Jython reports the .class file to the tracer, use the source file.
258 if filename.endswith("$py.class"):
259 filename = filename[:-9] + ".py"
261 canonical = self.file_locator.canonical_filename(filename)
263 # If the user specified source or include, then that's authoritative
264 # about the outer bound of what to measure and we don't have to apply
265 # any canned exclusions. If they didn't, then we have to exclude the
266 # stdlib and coverage.py directories.
267 if self.source_match:
268 if not self.source_match.match(canonical):
269 return None, "falls outside the --source trees"
270 elif self.include_match:
271 if not self.include_match.match(canonical):
272 return None, "falls outside the --include trees"
273 else:
274 # If we aren't supposed to trace installed code, then check if this
275 # is near the Python standard library and skip it if so.
276 if self.pylib_match and self.pylib_match.match(canonical):
277 return None, "is in the stdlib"
279 # We exclude the coverage code itself, since a little of it will be
280 # measured otherwise.
281 if self.cover_match and self.cover_match.match(canonical):
282 return None, "is part of coverage.py"
284 # Check the file against the omit pattern.
285 if self.omit_match and self.omit_match.match(canonical):
286 return None, "is inside an --omit pattern"
288 return canonical, "because we love you"
290 def _should_trace(self, filename, frame):
291 """Decide whether to trace execution in `filename`.
293 Calls `_should_trace_with_reason`, and returns just the decision.
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)
303 return canonical
305 def _warn(self, msg):
306 """Use `msg` as a warning."""
307 self._warnings.append(msg)
308 sys.stderr.write("Coverage.py warning: %s\n" % msg)
310 def _check_for_packages(self):
311 """Update the source_match matcher with latest imported packages."""
312 # Our self.source_pkgs attribute is a list of package names we want to
313 # measure. Each time through here, we see if we've imported any of
314 # them yet. If so, we add its file to source_match, and we don't have
315 # to look for that package any more.
316 if self.source_pkgs:
317 found = []
318 for pkg in self.source_pkgs:
319 try:
320 mod = sys.modules[pkg]
321 except KeyError:
322 continue
324 found.append(pkg)
326 try:
327 pkg_file = mod.__file__
328 except AttributeError:
329 pkg_file = None
330 else:
331 d, f = os.path.split(pkg_file)
332 if f.startswith('__init__'):
333 # This is actually a package, return the directory.
334 pkg_file = d
335 else:
336 pkg_file = self._source_for_file(pkg_file)
337 pkg_file = self.file_locator.canonical_filename(pkg_file)
338 if not os.path.exists(pkg_file):
339 pkg_file = None
341 if pkg_file:
342 self.source.append(pkg_file)
343 self.source_match.add(pkg_file)
344 else:
345 self._warn("Module %s has no Python source." % pkg)
347 for pkg in found:
348 self.source_pkgs.remove(pkg)
350 def use_cache(self, usecache):
351 """Control the use of a data file (incorrectly called a cache).
353 `usecache` is true or false, whether to read and write data on disk.
356 self.data.usefile(usecache)
358 def load(self):
359 """Load previously-collected coverage data from the data file."""
360 self.collector.reset()
361 self.data.read()
363 def start(self):
364 """Start measuring code coverage.
366 Coverage measurement actually occurs in functions called after `start`
367 is invoked. Statements in the same scope as `start` won't be measured.
369 Once you invoke `start`, you must also call `stop` eventually, or your
370 process might not shut down cleanly.
373 if self.run_suffix:
374 # Calling start() means we're running code, so use the run_suffix
375 # as the data_suffix when we eventually save the data.
376 self.data_suffix = self.run_suffix
377 if self.auto_data:
378 self.load()
380 # Create the matchers we need for _should_trace
381 if self.source or self.source_pkgs:
382 self.source_match = TreeMatcher(self.source)
383 else:
384 if self.cover_dir:
385 self.cover_match = TreeMatcher([self.cover_dir])
386 if self.pylib_dirs:
387 self.pylib_match = TreeMatcher(self.pylib_dirs)
388 if self.include:
389 self.include_match = FnmatchMatcher(self.include)
390 if self.omit:
391 self.omit_match = FnmatchMatcher(self.omit)
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)
399 if self.debug.should('sys'):
400 self.debug.write("Debugging info:")
401 self.debug.write_formatted_info(self.sysinfo())
403 self.collector.start()
404 self._started = True
405 self._measured = True
407 def stop(self):
408 """Stop measuring code coverage."""
409 self._started = False
410 self.collector.stop()
412 def _atexit(self):
413 """Clean up on process shutdown."""
414 if self._started:
415 self.stop()
416 if self.auto_data:
417 self.save()
419 def erase(self):
420 """Erase previously-collected coverage data.
422 This removes the in-memory data collected in this session as well as
423 discarding the data file.
426 self.collector.reset()
427 self.data.erase()
429 def clear_exclude(self, which='exclude'):
430 """Clear the exclude list."""
431 setattr(self.config, which + "_list", [])
432 self._exclude_regex_stale()
434 def exclude(self, regex, which='exclude'):
435 """Exclude source lines from execution consideration.
437 A number of lists of regular expressions are maintained. Each list
438 selects lines that are treated differently during reporting.
440 `which` determines which list is modified. The "exclude" list selects
441 lines that are not considered executable at all. The "partial" list
442 indicates lines with branches that are not taken.
444 `regex` is a regular expression. The regex is added to the specified
445 list. If any of the regexes in the list is found in a line, the line
446 is marked for special treatment during reporting.
449 excl_list = getattr(self.config, which + "_list")
450 excl_list.append(regex)
451 self._exclude_regex_stale()
453 def _exclude_regex_stale(self):
454 """Drop all the compiled exclusion regexes, a list was modified."""
455 self._exclude_re.clear()
457 def _exclude_regex(self, which):
458 """Return a compiled regex for the given exclusion list."""
459 if which not in self._exclude_re:
460 excl_list = getattr(self.config, which + "_list")
461 self._exclude_re[which] = join_regex(excl_list)
462 return self._exclude_re[which]
464 def get_exclude_list(self, which='exclude'):
465 """Return a list of excluded regex patterns.
467 `which` indicates which list is desired. See `exclude` for the lists
468 that are available, and their meaning.
471 return getattr(self.config, which + "_list")
473 def save(self):
474 """Save the collected coverage data to the data file."""
475 data_suffix = self.data_suffix
476 if data_suffix is True:
477 # If data_suffix was a simple true value, then make a suffix with
478 # plenty of distinguishing information. We do this here in
479 # `save()` at the last minute so that the pid will be correct even
480 # if the process forks.
481 extra = ""
482 if _TEST_NAME_FILE:
483 f = open(_TEST_NAME_FILE)
484 test_name = f.read()
485 f.close()
486 extra = "." + test_name
487 data_suffix = "%s%s.%s.%06d" % (
488 socket.gethostname(), extra, os.getpid(),
489 random.randint(0, 999999)
492 self._harvest_data()
493 self.data.write(suffix=data_suffix)
495 def combine(self):
496 """Combine together a number of similarly-named coverage data files.
498 All coverage data files whose name starts with `data_file` (from the
499 coverage() constructor) will be read, and combined together into the
500 current measurements.
503 aliases = None
504 if self.config.paths:
505 aliases = PathAliases(self.file_locator)
506 for paths in self.config.paths.values():
507 result = paths[0]
508 for pattern in paths[1:]:
509 aliases.add(pattern, result)
510 self.data.combine_parallel_data(aliases=aliases)
512 def _harvest_data(self):
513 """Get the collected data and reset the collector.
515 Also warn about various problems collecting data.
518 if not self._measured:
519 return
521 self.data.add_line_data(self.collector.get_line_data())
522 self.data.add_arc_data(self.collector.get_arc_data())
523 self.collector.reset()
525 # If there are still entries in the source_pkgs list, then we never
526 # encountered those packages.
527 if self._warn_unimported_source:
528 for pkg in self.source_pkgs:
529 self._warn("Module %s was never imported." % pkg)
531 # Find out if we got any data.
532 summary = self.data.summary()
533 if not summary and self._warn_no_data:
534 self._warn("No data was collected.")
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)
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
546 self.data.touch_file(py_file)
548 self._measured = False
550 # Backward compatibility with version 1.
551 def analysis(self, morf):
552 """Like `analysis2` but doesn't return excluded line numbers."""
553 f, s, _, m, mf = self.analysis2(morf)
554 return f, s, m, mf
556 def analysis2(self, morf):
557 """Analyze a module.
559 `morf` is a module or a filename. It will be analyzed to determine
560 its coverage statistics. The return value is a 5-tuple:
562 * The filename for the module.
563 * A list of line numbers of executable statements.
564 * A list of line numbers of excluded statements.
565 * A list of line numbers of statements not run (missing from
566 execution).
567 * A readable formatted string of the missing line numbers.
569 The analysis uses the source file itself and the current measured
570 coverage data.
573 analysis = self._analyze(morf)
574 return (
575 analysis.filename,
576 sorted(analysis.statements),
577 sorted(analysis.excluded),
578 sorted(analysis.missing),
579 analysis.missing_formatted(),
582 def _analyze(self, it):
583 """Analyze a single morf or code unit.
585 Returns an `Analysis` object.
588 self._harvest_data()
589 if not isinstance(it, CodeUnit):
590 it = code_unit_factory(it, self.file_locator)[0]
592 return Analysis(self, it)
594 def report(self, morfs=None, show_missing=True, ignore_errors=None,
595 file=None, # pylint: disable=W0622
596 omit=None, include=None
598 """Write a summary report to `file`.
600 Each module in `morfs` is listed, with counts of statements, executed
601 statements, missing statements, and a list of lines missed.
603 `include` is a list of filename patterns. Modules whose filenames
604 match those patterns will be included in the report. Modules matching
605 `omit` will not be included in the report.
607 Returns a float, the total percentage covered.
610 self._harvest_data()
611 self.config.from_args(
612 ignore_errors=ignore_errors, omit=omit, include=include,
613 show_missing=show_missing,
615 reporter = SummaryReporter(self, self.config)
616 return reporter.report(morfs, outfile=file)
618 def annotate(self, morfs=None, directory=None, ignore_errors=None,
619 omit=None, include=None):
620 """Annotate a list of modules.
622 Each module in `morfs` is annotated. The source is written to a new
623 file, named with a ",cover" suffix, with each line prefixed with a
624 marker to indicate the coverage of the line. Covered lines have ">",
625 excluded lines have "-", and missing lines have "!".
627 See `coverage.report()` for other arguments.
630 self._harvest_data()
631 self.config.from_args(
632 ignore_errors=ignore_errors, omit=omit, include=include
634 reporter = AnnotateReporter(self, self.config)
635 reporter.report(morfs, directory=directory)
637 def html_report(self, morfs=None, directory=None, ignore_errors=None,
638 omit=None, include=None, extra_css=None, title=None):
639 """Generate an HTML report.
641 The HTML is written to `directory`. The file "index.html" is the
642 overview starting point, with links to more detailed pages for
643 individual modules.
645 `extra_css` is a path to a file of other CSS to apply on the page.
646 It will be copied into the HTML directory.
648 `title` is a text string (not HTML) to use as the title of the HTML
649 report.
651 See `coverage.report()` for other arguments.
653 Returns a float, the total percentage covered.
656 self._harvest_data()
657 self.config.from_args(
658 ignore_errors=ignore_errors, omit=omit, include=include,
659 html_dir=directory, extra_css=extra_css, html_title=title,
661 reporter = HtmlReporter(self, self.config)
662 return reporter.report(morfs)
664 def xml_report(self, morfs=None, outfile=None, ignore_errors=None,
665 omit=None, include=None):
666 """Generate an XML report of coverage results.
668 The report is compatible with Cobertura reports.
670 Each module in `morfs` is included in the report. `outfile` is the
671 path to write the file to, "-" will write to stdout.
673 See `coverage.report()` for other arguments.
675 Returns a float, the total percentage covered.
678 self._harvest_data()
679 self.config.from_args(
680 ignore_errors=ignore_errors, omit=omit, include=include,
681 xml_output=outfile,
683 file_to_close = None
684 delete_file = False
685 if self.config.xml_output:
686 if self.config.xml_output == '-':
687 outfile = sys.stdout
688 else:
689 outfile = open(self.config.xml_output, "w")
690 file_to_close = outfile
691 try:
692 try:
693 reporter = XmlReporter(self, self.config)
694 return reporter.report(morfs, outfile=outfile)
695 except CoverageException:
696 delete_file = True
697 raise
698 finally:
699 if file_to_close:
700 file_to_close.close()
701 if delete_file:
702 file_be_gone(self.config.xml_output)
704 def sysinfo(self):
705 """Return a list of (key, value) pairs showing internal information."""
707 import coverage as covmod
708 import platform, re
710 try:
711 implementation = platform.python_implementation()
712 except AttributeError:
713 implementation = "unknown"
715 info = [
716 ('version', covmod.__version__),
717 ('coverage', covmod.__file__),
718 ('cover_dir', self.cover_dir),
719 ('pylib_dirs', self.pylib_dirs),
720 ('tracer', self.collector.tracer_name()),
721 ('config_files', self.config.attempted_config_files),
722 ('configs_read', self.config.config_files),
723 ('data_path', self.data.filename),
724 ('python', sys.version.replace('\n', '')),
725 ('platform', platform.platform()),
726 ('implementation', implementation),
727 ('executable', sys.executable),
728 ('cwd', os.getcwd()),
729 ('path', sys.path),
730 ('environment', sorted([
731 ("%s = %s" % (k, v)) for k, v in iitems(os.environ)
732 if re.search(r"^COV|^PY", k)
733 ])),
734 ('command_line', " ".join(getattr(sys, 'argv', ['???']))),
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()))
747 return info
750 def process_startup():
751 """Call this at Python startup to perhaps measure coverage.
753 If the environment variable COVERAGE_PROCESS_START is defined, coverage
754 measurement is started. The value of the variable is the config file
755 to use.
757 There are two ways to configure your Python installation to invoke this
758 function when Python starts:
760 #. Create or append to sitecustomize.py to add these lines::
762 import coverage
763 coverage.process_startup()
765 #. Create a .pth file in your Python installation containing::
767 import coverage; coverage.process_startup()
770 cps = os.environ.get("COVERAGE_PROCESS_START")
771 if cps:
772 cov = coverage(config_file=cps, auto_data=True)
773 cov.start()
774 cov._warn_no_data = False
775 cov._warn_unimported_source = False
778 # A hack for debugging testing in subprocesses.
779 _TEST_NAME_FILE = "" #"/tmp/covtest.txt"