Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(702)

Side by Side Diff: third_party/pycoverage/coverage/control.py

Issue 727003004: Add python coverage module to third_party (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pycoverage/coverage/config.py ('k') | third_party/pycoverage/coverage/data.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 """Core control stuff for Coverage."""
2
3 import atexit, os, random, socket, sys
4
5 from coverage.annotate import AnnotateReporter
6 from coverage.backward import string_class, iitems, sorted # pylint: disable=W0 622
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
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
29 class coverage(object):
30 """Programmatic access to coverage.py.
31
32 To use::
33
34 from coverage import coverage
35
36 cov = coverage()
37 cov.start()
38 #.. call your code ..
39 cov.stop()
40 cov.html_report(directory='covhtml')
41
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.
52
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.
56
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.
60
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.
64
65 If `branch` is true, then branch coverage will be measured in addition
66 to the usual statement coverage.
67
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.
72
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.
76
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.
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
85 """
86 from coverage import __version__
87
88 # A record of all the warnings that have been issued.
89 self._warnings = []
90
91 # Build our configuration from a number of sources:
92 # 1: defaults:
93 self.config = CoverageConfig()
94
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)
105 )
106
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
112
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,
118 )
119
120 # Create and configure the debugging controller.
121 self.debug = DebugControl(self.config.debug, debug_file or sys.stderr)
122
123 self.auto_data = auto_data
124
125 # _exclude_re is a dict mapping exclusion list names to compiled
126 # regexes.
127 self._exclude_re = {}
128 self._exclude_regex_stale()
129
130 self.file_locator = FileLocator()
131
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)
140
141 self.omit = prep_patterns(self.config.omit)
142 self.include = prep_patterns(self.config.include)
143
144 self.collector = Collector(
145 self._should_trace, timid=self.config.timid,
146 branch=self.config.branch, warn=self._warn
147 )
148
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
161
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,
169 )
170
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)
184
185 # To avoid tracing the coverage code itself, we skip anything located
186 # where we are.
187 self.cover_dir = self._canonical_dir(__file__)
188
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
193
194 # Set the reporting precision.
195 Numbers.set_precision(self.config.precision)
196
197 # Is it ok for no data to be collected?
198 self._warn_no_data = True
199 self._warn_unimported_source = True
200
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
206
207 atexit.register(self._atexit)
208
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]
212
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
221
222 def _should_trace_with_reason(self, filename, frame):
223 """Decide whether to trace execution in `filename`, with a reason.
224
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.
227
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.
232
233 """
234 if not filename:
235 # Empty string is pretty useless
236 return None, "empty string isn't a filename"
237
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"
244
245 self._check_for_packages()
246
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)
256
257 # Jython reports the .class file to the tracer, use the source file.
258 if filename.endswith("$py.class"):
259 filename = filename[:-9] + ".py"
260
261 canonical = self.file_locator.canonical_filename(filename)
262
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"
278
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"
283
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"
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)
303 return canonical
304
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)
309
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
323
324 found.append(pkg)
325
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
340
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)
346
347 for pkg in found:
348 self.source_pkgs.remove(pkg)
349
350 def use_cache(self, usecache):
351 """Control the use of a data file (incorrectly called a cache).
352
353 `usecache` is true or false, whether to read and write data on disk.
354
355 """
356 self.data.usefile(usecache)
357
358 def load(self):
359 """Load previously-collected coverage data from the data file."""
360 self.collector.reset()
361 self.data.read()
362
363 def start(self):
364 """Start measuring code coverage.
365
366 Coverage measurement actually occurs in functions called after `start`
367 is invoked. Statements in the same scope as `start` won't be measured.
368
369 Once you invoke `start`, you must also call `stop` eventually, or your
370 process might not shut down cleanly.
371
372 """
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()
379
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)
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
403 self.collector.start()
404 self._started = True
405 self._measured = True
406
407 def stop(self):
408 """Stop measuring code coverage."""
409 self._started = False
410 self.collector.stop()
411
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()
418
419 def erase(self):
420 """Erase previously-collected coverage data.
421
422 This removes the in-memory data collected in this session as well as
423 discarding the data file.
424
425 """
426 self.collector.reset()
427 self.data.erase()
428
429 def clear_exclude(self, which='exclude'):
430 """Clear the exclude list."""
431 setattr(self.config, which + "_list", [])
432 self._exclude_regex_stale()
433
434 def exclude(self, regex, which='exclude'):
435 """Exclude source lines from execution consideration.
436
437 A number of lists of regular expressions are maintained. Each list
438 selects lines that are treated differently during reporting.
439
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.
443
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.
447
448 """
449 excl_list = getattr(self.config, which + "_list")
450 excl_list.append(regex)
451 self._exclude_regex_stale()
452
453 def _exclude_regex_stale(self):
454 """Drop all the compiled exclusion regexes, a list was modified."""
455 self._exclude_re.clear()
456
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]
463
464 def get_exclude_list(self, which='exclude'):
465 """Return a list of excluded regex patterns.
466
467 `which` indicates which list is desired. See `exclude` for the lists
468 that are available, and their meaning.
469
470 """
471 return getattr(self.config, which + "_list")
472
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)
490 )
491
492 self._harvest_data()
493 self.data.write(suffix=data_suffix)
494
495 def combine(self):
496 """Combine together a number of similarly-named coverage data files.
497
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.
501
502 """
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)
511
512 def _harvest_data(self):
513 """Get the collected data and reset the collector.
514
515 Also warn about various problems collecting data.
516
517 """
518 if not self._measured:
519 return
520
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()
524
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)
530
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.")
535
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
549
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
555
556 def analysis2(self, morf):
557 """Analyze a module.
558
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:
561
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.
568
569 The analysis uses the source file itself and the current measured
570 coverage data.
571
572 """
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(),
580 )
581
582 def _analyze(self, it):
583 """Analyze a single morf or code unit.
584
585 Returns an `Analysis` object.
586
587 """
588 self._harvest_data()
589 if not isinstance(it, CodeUnit):
590 it = code_unit_factory(it, self.file_locator)[0]
591
592 return Analysis(self, it)
593
594 def report(self, morfs=None, show_missing=True, ignore_errors=None,
595 file=None, # pylint: disable=W0622
596 omit=None, include=None
597 ):
598 """Write a summary report to `file`.
599
600 Each module in `morfs` is listed, with counts of statements, executed
601 statements, missing statements, and a list of lines missed.
602
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.
606
607 Returns a float, the total percentage covered.
608
609 """
610 self._harvest_data()
611 self.config.from_args(
612 ignore_errors=ignore_errors, omit=omit, include=include,
613 show_missing=show_missing,
614 )
615 reporter = SummaryReporter(self, self.config)
616 return reporter.report(morfs, outfile=file)
617
618 def annotate(self, morfs=None, directory=None, ignore_errors=None,
619 omit=None, include=None):
620 """Annotate a list of modules.
621
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 "!".
626
627 See `coverage.report()` for other arguments.
628
629 """
630 self._harvest_data()
631 self.config.from_args(
632 ignore_errors=ignore_errors, omit=omit, include=include
633 )
634 reporter = AnnotateReporter(self, self.config)
635 reporter.report(morfs, directory=directory)
636
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.
640
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.
644
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.
647
648 `title` is a text string (not HTML) to use as the title of the HTML
649 report.
650
651 See `coverage.report()` for other arguments.
652
653 Returns a float, the total percentage covered.
654
655 """
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,
660 )
661 reporter = HtmlReporter(self, self.config)
662 return reporter.report(morfs)
663
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.
667
668 The report is compatible with Cobertura reports.
669
670 Each module in `morfs` is included in the report. `outfile` is the
671 path to write the file to, "-" will write to stdout.
672
673 See `coverage.report()` for other arguments.
674
675 Returns a float, the total percentage covered.
676
677 """
678 self._harvest_data()
679 self.config.from_args(
680 ignore_errors=ignore_errors, omit=omit, include=include,
681 xml_output=outfile,
682 )
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)
703
704 def sysinfo(self):
705 """Return a list of (key, value) pairs showing internal information."""
706
707 import coverage as covmod
708 import platform, re
709
710 try:
711 implementation = platform.python_implementation()
712 except AttributeError:
713 implementation = "unknown"
714
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', ['???']))),
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
747 return info
748
749
750 def process_startup():
751 """Call this at Python startup to perhaps measure coverage.
752
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.
756
757 There are two ways to configure your Python installation to invoke this
758 function when Python starts:
759
760 #. Create or append to sitecustomize.py to add these lines::
761
762 import coverage
763 coverage.process_startup()
764
765 #. Create a .pth file in your Python installation containing::
766
767 import coverage; coverage.process_startup()
768
769 """
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
776
777
778 # A hack for debugging testing in subprocesses.
779 _TEST_NAME_FILE = "" #"/tmp/covtest.txt"
OLDNEW
« no previous file with comments | « third_party/pycoverage/coverage/config.py ('k') | third_party/pycoverage/coverage/data.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698