diff options
-rw-r--r-- | coverage/collector.py | 13 | ||||
-rw-r--r-- | coverage/control.py | 72 | ||||
-rw-r--r-- | coverage/tracer.c | 24 |
3 files changed, 67 insertions, 42 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index 07a87e03..e3f4f630 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -89,19 +89,20 @@ class PyTracer(object): self.data_stack.append((self.handler, self.cur_file_data, self.last_line)) filename = frame.f_code.co_filename if filename not in self.should_trace_cache: - tracename, handler = self.should_trace(filename, frame) - self.should_trace_cache[filename] = tracename, handler + disp = self.should_trace(filename, frame) + self.should_trace_cache[filename] = disp else: - tracename, handler = self.should_trace_cache[filename] + disp = self.should_trace_cache[filename] #print("called, stack is %d deep, tracename is %r" % ( # len(self.data_stack), tracename)) - if tracename and handler: - tracename = handler.file_name(frame) + tracename = disp.filename + if tracename and disp.handler: + tracename = disp.handler.file_name(frame) if tracename: if tracename not in self.data: self.data[tracename] = {} self.cur_file_data = self.data[tracename] - self.handler = handler + self.handler = disp.handler else: self.cur_file_data = None # Set the last_line to -1 because the next arc will be entering a diff --git a/coverage/control.py b/coverage/control.py index a65a7153..d6bd6092 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -237,32 +237,24 @@ class coverage(object): 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 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. + Returns a FileDisposition object. """ + disp = FileDisposition(filename) + if not filename: # Empty string is pretty useless - return None, "empty string isn't a filename" + return disp.nope("empty string isn't a filename") if filename.startswith('memory:'): - if 0: - import dis, sys, StringIO - _stdout = sys.stdout - sys.stdout = new_stdout = StringIO.StringIO() - dis.dis(frame.f_code) - sys.stdout = _stdout - return None, new_stdout.getvalue() - return None, "memory isn't traceable" + return disp.nope("memory isn't traceable") 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 None, "not a real filename" + return disp.nope("not a real filename") self._check_for_packages() @@ -284,7 +276,9 @@ class coverage(object): # DJANGO HACK if self.django_tracer.should_trace(canonical): - return canonical, self.django_tracer + disp.filename = canonical + disp.handler = self.django_tracer + return disp # If the user specified source or include, then that's authoritative # about the outer bound of what to measure and we don't have to apply @@ -292,42 +286,38 @@ class coverage(object): # stdlib and coverage.py directories. if self.source_match: if not self.source_match.match(canonical): - return None, "falls outside the --source trees" + return disp.nope("falls outside the --source trees") elif self.include_match: if not self.include_match.match(canonical): - return None, "falls outside the --include trees" + return disp.nope("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 None, "is in the stdlib" + return disp.nope("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 None, "is part of coverage.py" + return disp.nope("is part of coverage.py") # Check the file against the omit pattern. if self.omit_match and self.omit_match.match(canonical): - return None, "is inside an --omit pattern" + return disp.nope("is inside an --omit pattern") - return canonical, None + disp.filename = canonical + return disp def _should_trace(self, filename, frame): """Decide whether to trace execution in `filename`. - Calls `_should_trace_with_reason`, and returns just the decision. + Calls `_should_trace_with_reason`, and returns the FileDisposition. """ - canonical, other = self._should_trace_with_reason(filename, frame) + disp = self._should_trace_with_reason(filename, frame) if self.debug.should('trace'): - if not canonical: - msg = "Not tracing %r: %s" % (filename, other) - other = None - else: - msg = "Tracing %r" % (filename,) - self.debug.write(msg) - return canonical, other + self.debug.write(disp.debug_message()) + return disp def _warn(self, msg): """Use `msg` as a warning.""" @@ -780,6 +770,28 @@ class coverage(object): return info +class FileDisposition(object): + """A simple object for noting a number of details of files to trace.""" + def __init__(self, original_filename): + self.original_filename = original_filename + self.filename = None + self.reason = "" + self.handler = None + + def nope(self, reason): + """A helper for return a NO answer from should_trace.""" + self.reason = reason + return self + + def debug_message(self): + """Produce a debugging message explaining the outcome.""" + if not self.filename: + msg = "Not tracing %r: %s" % (self.original_filename, self.reason) + else: + msg = "Tracing %r" % (self.original_filename,) + return msg + + def process_startup(): """Call this at Python startup to perhaps measure coverage. diff --git a/coverage/tracer.c b/coverage/tracer.c index 97dd113b..ca8d61c1 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -259,6 +259,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse int ret = RET_OK; PyObject * filename = NULL; PyObject * tracename = NULL; + PyObject * disposition = NULL; #if WHAT_LOG || TRACE_LOG PyObject * ascii = NULL; #endif @@ -335,41 +336,51 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse /* Check if we should trace this line. */ filename = frame->f_code->co_filename; - tracename = PyDict_GetItem(self->should_trace_cache, filename); - if (tracename == NULL) { + disposition = PyDict_GetItem(self->should_trace_cache, filename); + if (disposition == NULL) { STATS( self->stats.new_files++; ) /* We've never considered this file before. */ /* Ask should_trace about it. */ PyObject * args = Py_BuildValue("(OO)", filename, frame); - tracename = PyObject_Call(self->should_trace, args, NULL); + disposition = PyObject_Call(self->should_trace, args, NULL); Py_DECREF(args); - if (tracename == NULL) { + if (disposition == NULL) { /* An error occurred inside should_trace. */ STATS( self->stats.errors++; ) return RET_ERROR; } - if (PyDict_SetItem(self->should_trace_cache, filename, tracename) < 0) { + if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) { STATS( self->stats.errors++; ) return RET_ERROR; } } else { - Py_INCREF(tracename); + Py_INCREF(disposition); } /* If tracename is a string, then we're supposed to trace. */ + tracename = PyObject_GetAttrString(disposition, "filename"); + if (tracename == NULL) { + STATS( self->stats.errors++; ) + Py_DECREF(disposition); + return RET_ERROR; + } if (MyText_Check(tracename)) { PyObject * file_data = PyDict_GetItem(self->data, tracename); if (file_data == NULL) { file_data = PyDict_New(); if (file_data == NULL) { STATS( self->stats.errors++; ) + Py_DECREF(tracename); + Py_DECREF(disposition); return RET_ERROR; } ret = PyDict_SetItem(self->data, tracename, file_data); Py_DECREF(file_data); if (ret < 0) { STATS( self->stats.errors++; ) + Py_DECREF(tracename); + Py_DECREF(disposition); return RET_ERROR; } } @@ -385,6 +396,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse } Py_DECREF(tracename); + Py_DECREF(disposition); self->last_line = -1; break; |