From 1ffbdc3328de90783d0ba962838b3f7924adf77c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 16 Aug 2013 21:59:20 -0400 Subject: Keep things working on PyPy 2.1 _structseq is in the lib_pypy directory on sys.path, but for some reason when imported, it claims to be from /opt/pypy//pypy-c-jit-linux-x86-64/build/lib_pypy/_structseq.py I don't know why that is, but importing _structseq and treating its directory as part of the stdlib makes the tests pass. --- coverage/control.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 8821bfb..98e4e78 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -17,6 +17,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. @@ -158,8 +166,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) -- cgit v1.2.1 From f94f6a874e8466ecd825b796419606225a770668 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 4 Sep 2013 09:25:13 -0400 Subject: New flag for run: --debug, can trace why files aren't being traced. --- coverage/control.py | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 98e4e78..fa85be6 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -41,7 +41,7 @@ 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=()): """ `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 @@ -76,6 +76,9 @@ class coverage(object): `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. + `debug` is a sequence of strings indicating what debugging information + is desired. + """ from coverage import __version__ @@ -108,7 +111,7 @@ 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, ) self.auto_data = auto_data @@ -209,26 +212,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 "", "", or # "". 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() @@ -254,35 +259,38 @@ 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`. + + Calls `_should_trace_with_reason`, and returns just the decision. - # 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 + """ + canonical, reason = self._should_trace_with_reason(filename, frame) + if not canonical: + if 'notrace' in self.config.debug: + sys.stderr.write("Not tracing %r: %s\n" % (filename, reason)) + return canonical def _warn(self, msg): """Use `msg` as a warning.""" -- cgit v1.2.1 From c45f4f848fac4634cad94f9cf5b35f0409e8c011 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 6 Sep 2013 20:50:01 -0400 Subject: Make debug=trace trace all the yes/no tracing decisions. --- coverage/control.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index fa85be6..a767a26 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -41,7 +41,7 @@ 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, debug=()): + source=None, omit=None, include=None, debug=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 @@ -287,9 +287,12 @@ class coverage(object): """ canonical, reason = self._should_trace_with_reason(filename, frame) - if not canonical: - if 'notrace' in self.config.debug: - sys.stderr.write("Not tracing %r: %s\n" % (filename, reason)) + if 'trace' in self.config.debug: + if not canonical: + msg = "Not tracing %r: %s\n" % (filename, reason) + else: + msg = "Tracing %r\n" % (filename,) + sys.stderr.write(msg) return canonical def _warn(self, msg): -- cgit v1.2.1 From 83411f84debeefa313a1b61121ef4fc9c9607fd6 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 8 Sep 2013 14:49:35 -0400 Subject: More --debug options, split code into separate objects. --- coverage/control.py | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index a767a26..4b76121 100644 --- a/coverage/control.py +++ b/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 @@ -41,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, debug=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 @@ -76,8 +78,9 @@ class coverage(object): `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. - `debug` is a sequence of strings indicating what debugging information - is desired. + `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__ @@ -114,6 +117,9 @@ class coverage(object): 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 @@ -158,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". @@ -179,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 @@ -287,12 +294,12 @@ class coverage(object): """ canonical, reason = self._should_trace_with_reason(filename, frame) - if 'trace' in self.config.debug: + if self.debug.should('trace'): if not canonical: - msg = "Not tracing %r: %s\n" % (filename, reason) + msg = "Not tracing %r: %s" % (filename, reason) else: - msg = "Tracing %r\n" % (filename,) - sys.stderr.write(msg) + msg = "Tracing %r" % (filename,) + self.debug.write(msg) return canonical def _warn(self, msg): @@ -383,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 @@ -707,11 +724,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 -- cgit v1.2.1