| Index: third_party/coverage-3.7.1/coverage/control.py
|
| diff --git a/third_party/coverage-3.6/coverage/control.py b/third_party/coverage-3.7.1/coverage/control.py
|
| similarity index 84%
|
| rename from third_party/coverage-3.6/coverage/control.py
|
| rename to third_party/coverage-3.7.1/coverage/control.py
|
| index afb61370c505d4a2e48a60bccd22231ce2815d74..f75a3dda5b1b479a45e52c50d1390b7d076a8b1f 100644
|
| --- a/third_party/coverage-3.6/coverage/control.py
|
| +++ b/third_party/coverage-3.7.1/coverage/control.py
|
| @@ -3,11 +3,12 @@
|
| import atexit, os, random, socket, sys
|
|
|
| from coverage.annotate import AnnotateReporter
|
| -from coverage.backward import string_class, iitems
|
| +from coverage.backward import string_class, iitems, sorted # pylint: disable=W0622
|
| from coverage.codeunit import code_unit_factory, CodeUnit
|
| from coverage.collector import Collector
|
| from coverage.config import CoverageConfig
|
| from coverage.data import CoverageData
|
| +from coverage.debug import DebugControl
|
| from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
|
| from coverage.files import PathAliases, find_python_files, prep_patterns
|
| from coverage.html import HtmlReporter
|
| @@ -17,6 +18,14 @@ from coverage.results import Analysis, Numbers
|
| from coverage.summary import SummaryReporter
|
| from coverage.xmlreport import XmlReporter
|
|
|
| +# Pypy has some unusual stuff in the "stdlib". Consider those locations
|
| +# when deciding where the stdlib is.
|
| +try:
|
| + import _structseq # pylint: disable=F0401
|
| +except ImportError:
|
| + _structseq = None
|
| +
|
| +
|
| class coverage(object):
|
| """Programmatic access to coverage.py.
|
|
|
| @@ -33,7 +42,8 @@ class coverage(object):
|
| """
|
| def __init__(self, data_file=None, data_suffix=None, cover_pylib=None,
|
| auto_data=False, timid=None, branch=None, config_file=True,
|
| - source=None, omit=None, include=None):
|
| + source=None, omit=None, include=None, debug=None,
|
| + debug_file=None):
|
| """
|
| `data_file` is the base name of the data file to use, defaulting to
|
| ".coverage". `data_suffix` is appended (with a dot) to `data_file` to
|
| @@ -68,6 +78,10 @@ class coverage(object):
|
| `include` will be measured, files that match `omit` will not. Each
|
| will also accept a single string argument.
|
|
|
| + `debug` is a list of strings indicating what debugging information is
|
| + desired. `debug_file` is the file to write debug messages to,
|
| + defaulting to stderr.
|
| +
|
| """
|
| from coverage import __version__
|
|
|
| @@ -100,9 +114,12 @@ class coverage(object):
|
| self.config.from_args(
|
| data_file=data_file, cover_pylib=cover_pylib, timid=timid,
|
| branch=branch, parallel=bool_or_none(data_suffix),
|
| - source=source, omit=omit, include=include
|
| + source=source, omit=omit, include=include, debug=debug,
|
| )
|
|
|
| + # Create and configure the debugging controller.
|
| + self.debug = DebugControl(self.config.debug, debug_file or sys.stderr)
|
| +
|
| self.auto_data = auto_data
|
|
|
| # _exclude_re is a dict mapping exclusion list names to compiled
|
| @@ -147,7 +164,8 @@ class coverage(object):
|
| # started rather than wherever the process eventually chdir'd to.
|
| self.data = CoverageData(
|
| basename=self.config.data_file,
|
| - collector="coverage v%s" % __version__
|
| + collector="coverage v%s" % __version__,
|
| + debug=self.debug,
|
| )
|
|
|
| # The dirs for files considered "installed with the interpreter".
|
| @@ -158,8 +176,8 @@ class coverage(object):
|
| # environments (virtualenv, for example), these modules may be
|
| # spread across a few locations. Look at all the candidate modules
|
| # we've imported, and take all the different ones.
|
| - for m in (atexit, os, random, socket):
|
| - if hasattr(m, "__file__"):
|
| + for m in (atexit, os, random, socket, _structseq):
|
| + if m is not None and hasattr(m, "__file__"):
|
| m_dir = self._canonical_dir(m)
|
| if m_dir not in self.pylib_dirs:
|
| self.pylib_dirs.append(m_dir)
|
| @@ -168,7 +186,7 @@ class coverage(object):
|
| # where we are.
|
| self.cover_dir = self._canonical_dir(__file__)
|
|
|
| - # The matchers for _should_trace, created when tracing starts.
|
| + # The matchers for _should_trace.
|
| self.source_match = None
|
| self.pylib_match = self.cover_match = None
|
| self.include_match = self.omit_match = None
|
| @@ -201,26 +219,28 @@ class coverage(object):
|
| filename = filename[:-9] + ".py"
|
| return filename
|
|
|
| - def _should_trace(self, filename, frame):
|
| - """Decide whether to trace execution in `filename`
|
| + def _should_trace_with_reason(self, filename, frame):
|
| + """Decide whether to trace execution in `filename`, with a reason.
|
|
|
| This function is called from the trace function. As each new file name
|
| is encountered, this function determines whether it is traced or not.
|
|
|
| - Returns a canonicalized filename if it should be traced, False if it
|
| - should not.
|
| + Returns a pair of values: the first indicates whether the file should
|
| + be traced: it's a canonicalized filename if it should be traced, None
|
| + if it should not. The second value is a string, the resason for the
|
| + decision.
|
|
|
| """
|
| if not filename:
|
| # Empty string is pretty useless
|
| - return False
|
| + return None, "empty string isn't a filename"
|
|
|
| if filename.startswith('<'):
|
| # Lots of non-file execution is represented with artificial
|
| # filenames like "<string>", "<doctest readme.txt[0]>", or
|
| # "<exec_function>". Don't ever trace these executions, since we
|
| # can't do anything with the data later anyway.
|
| - return False
|
| + return None, "not a real filename"
|
|
|
| self._check_for_packages()
|
|
|
| @@ -246,35 +266,41 @@ class coverage(object):
|
| # stdlib and coverage.py directories.
|
| if self.source_match:
|
| if not self.source_match.match(canonical):
|
| - return False
|
| + return None, "falls outside the --source trees"
|
| elif self.include_match:
|
| if not self.include_match.match(canonical):
|
| - return False
|
| + return None, "falls outside the --include trees"
|
| else:
|
| # If we aren't supposed to trace installed code, then check if this
|
| # is near the Python standard library and skip it if so.
|
| if self.pylib_match and self.pylib_match.match(canonical):
|
| - return False
|
| + return None, "is in the stdlib"
|
|
|
| # We exclude the coverage code itself, since a little of it will be
|
| # measured otherwise.
|
| if self.cover_match and self.cover_match.match(canonical):
|
| - return False
|
| + return None, "is part of coverage.py"
|
|
|
| # Check the file against the omit pattern.
|
| if self.omit_match and self.omit_match.match(canonical):
|
| - return False
|
| + return None, "is inside an --omit pattern"
|
|
|
| - return canonical
|
| + return canonical, "because we love you"
|
| +
|
| + def _should_trace(self, filename, frame):
|
| + """Decide whether to trace execution in `filename`.
|
|
|
| - # To log what should_trace returns, change this to "if 1:"
|
| - if 0:
|
| - _real_should_trace = _should_trace
|
| - def _should_trace(self, filename, frame): # pylint: disable=E0102
|
| - """A logging decorator around the real _should_trace function."""
|
| - ret = self._real_should_trace(filename, frame)
|
| - print("should_trace: %r -> %r" % (filename, ret))
|
| - return ret
|
| + Calls `_should_trace_with_reason`, and returns just the decision.
|
| +
|
| + """
|
| + canonical, reason = self._should_trace_with_reason(filename, frame)
|
| + if self.debug.should('trace'):
|
| + if not canonical:
|
| + msg = "Not tracing %r: %s" % (filename, reason)
|
| + else:
|
| + msg = "Tracing %r" % (filename,)
|
| + self.debug.write(msg)
|
| + return canonical
|
|
|
| def _warn(self, msg):
|
| """Use `msg` as a warning."""
|
| @@ -364,6 +390,16 @@ class coverage(object):
|
| if self.omit:
|
| self.omit_match = FnmatchMatcher(self.omit)
|
|
|
| + # The user may want to debug things, show info if desired.
|
| + if self.debug.should('config'):
|
| + self.debug.write("Configuration values:")
|
| + config_info = sorted(self.config.__dict__.items())
|
| + self.debug.write_formatted_info(config_info)
|
| +
|
| + if self.debug.should('sys'):
|
| + self.debug.write("Debugging info:")
|
| + self.debug.write_formatted_info(self.sysinfo())
|
| +
|
| self.collector.start()
|
| self._started = True
|
| self._measured = True
|
| @@ -479,29 +515,37 @@ class coverage(object):
|
| Also warn about various problems collecting data.
|
|
|
| """
|
| - if self._measured:
|
| - self.data.add_line_data(self.collector.get_line_data())
|
| - self.data.add_arc_data(self.collector.get_arc_data())
|
| - self.collector.reset()
|
| -
|
| - # If there are still entries in the source_pkgs list, then we never
|
| - # encountered those packages.
|
| - if self._warn_unimported_source:
|
| - for pkg in self.source_pkgs:
|
| - self._warn("Module %s was never imported." % pkg)
|
| -
|
| - # Find out if we got any data.
|
| - summary = self.data.summary()
|
| - if not summary and self._warn_no_data:
|
| - self._warn("No data was collected.")
|
| -
|
| - # Find files that were never executed at all.
|
| - for src in self.source:
|
| - for py_file in find_python_files(src):
|
| - py_file = self.file_locator.canonical_filename(py_file)
|
| - self.data.touch_file(py_file)
|
| -
|
| - self._measured = False
|
| + if not self._measured:
|
| + return
|
| +
|
| + self.data.add_line_data(self.collector.get_line_data())
|
| + self.data.add_arc_data(self.collector.get_arc_data())
|
| + self.collector.reset()
|
| +
|
| + # If there are still entries in the source_pkgs list, then we never
|
| + # encountered those packages.
|
| + if self._warn_unimported_source:
|
| + for pkg in self.source_pkgs:
|
| + self._warn("Module %s was never imported." % pkg)
|
| +
|
| + # Find out if we got any data.
|
| + summary = self.data.summary()
|
| + if not summary and self._warn_no_data:
|
| + self._warn("No data was collected.")
|
| +
|
| + # Find files that were never executed at all.
|
| + for src in self.source:
|
| + for py_file in find_python_files(src):
|
| + py_file = self.file_locator.canonical_filename(py_file)
|
| +
|
| + if self.omit_match and self.omit_match.match(py_file):
|
| + # Turns out this file was omitted, so don't pull it back
|
| + # in as unexecuted.
|
| + continue
|
| +
|
| + self.data.touch_file(py_file)
|
| +
|
| + self._measured = False
|
|
|
| # Backward compatibility with version 1.
|
| def analysis(self, morf):
|
| @@ -528,8 +572,11 @@ class coverage(object):
|
| """
|
| analysis = self._analyze(morf)
|
| return (
|
| - analysis.filename, analysis.statements, analysis.excluded,
|
| - analysis.missing, analysis.missing_formatted()
|
| + analysis.filename,
|
| + sorted(analysis.statements),
|
| + sorted(analysis.excluded),
|
| + sorted(analysis.missing),
|
| + analysis.missing_formatted(),
|
| )
|
|
|
| def _analyze(self, it):
|
| @@ -680,11 +727,23 @@ class coverage(object):
|
| ('executable', sys.executable),
|
| ('cwd', os.getcwd()),
|
| ('path', sys.path),
|
| - ('environment', [
|
| + ('environment', sorted([
|
| ("%s = %s" % (k, v)) for k, v in iitems(os.environ)
|
| if re.search(r"^COV|^PY", k)
|
| - ]),
|
| + ])),
|
| + ('command_line', " ".join(getattr(sys, 'argv', ['???']))),
|
| ]
|
| + if self.source_match:
|
| + info.append(('source_match', self.source_match.info()))
|
| + if self.include_match:
|
| + info.append(('include_match', self.include_match.info()))
|
| + if self.omit_match:
|
| + info.append(('omit_match', self.omit_match.info()))
|
| + if self.cover_match:
|
| + info.append(('cover_match', self.cover_match.info()))
|
| + if self.pylib_match:
|
| + info.append(('pylib_match', self.pylib_match.info()))
|
| +
|
| return info
|
|
|
|
|
|
|