diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/collector.py | 7 | ||||
-rw-r--r-- | coverage/fullcoverage/encodings.py | 15 | ||||
-rw-r--r-- | coverage/tracer.c | 33 |
3 files changed, 43 insertions, 12 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index 9752e530..5498cc6c 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -234,7 +234,7 @@ class Collector(object): self._collectors.append(self) #print >>sys.stderr, "Started: %r" % self._collectors - # Check to see whether we had a fullcoverage tracer installed. + # Check to see whether we had a fullcoverage tracer installed. traces0 = None if hasattr(sys, "gettrace"): fn0 = sys.gettrace() @@ -247,10 +247,9 @@ class Collector(object): fn = self._start_tracer() if traces0: - #print("traces0 has %d" % len(traces0)) for args in traces0: - frame, event, arg = args - fn(*args) + (frame, event, arg), lineno = args + fn(frame, event, arg, lineno=lineno) # Install our installation tracer in threading, to jump start other # threads. diff --git a/coverage/fullcoverage/encodings.py b/coverage/fullcoverage/encodings.py index 48b2b2e0..4e1ab354 100644 --- a/coverage/fullcoverage/encodings.py +++ b/coverage/fullcoverage/encodings.py @@ -17,13 +17,22 @@ import sys class FullCoverageTracer(object): def __init__(self): + # `traces` is a list of trace events. Frames are tricky: the same + # frame object is used for a whole scope, with new line numbers + # written into it. So in one scope, all the frame objects are the + # same object, and will eventually all will point to the last line + # executed. So we keep the line numbers alongside the frames. + # The list looks like: + # + # traces = [ + # ((frame, event, arg), lineno), ... + # ] + # self.traces = [] def fullcoverage_trace(self, *args): frame, event, arg = args - #if "os.py" in frame.f_code.co_filename: - # print("%s @ %d" % (frame.f_code.co_filename, frame.f_lineno)) - self.traces.append(args) + self.traces.append((args, frame.f_lineno)) return self.fullcoverage_trace sys.settrace(FullCoverageTracer().fullcoverage_trace) diff --git a/coverage/tracer.c b/coverage/tracer.c index a7a4f418..e9fc56b4 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -468,14 +468,26 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) * means it must be callable to be used in sys.settrace(). * * So we make our self callable, equivalent to invoking our trace function. + * + * To help with the process of replaying stored frames, this function has an + * optional keyword argument: + * + * def Tracer_call(frame, event, arg, lineno=0) + * + * If provided, the lineno argument is used as the line number, and the + * frame's f_lineno member is ignored. */ static PyObject * -Tracer_call(Tracer *self, PyObject *args, PyObject *kwds_unused) +Tracer_call(Tracer *self, PyObject *args, PyObject *kwds) { PyFrameObject *frame; PyObject *what_str; PyObject *arg; + int lineno = 0; int what; + int orig_lineno; + PyObject *ret = NULL; + static char *what_names[] = { "call", "exception", "line", "return", "c_call", "c_exception", "c_return", @@ -486,8 +498,10 @@ Tracer_call(Tracer *self, PyObject *args, PyObject *kwds_unused) printf("pytrace\n"); #endif - if (!PyArg_ParseTuple(args, "O!O!O:Tracer_call", - &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg)) { + static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist, + &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) { goto done; } @@ -499,14 +513,23 @@ Tracer_call(Tracer *self, PyObject *args, PyObject *kwds_unused) } } + /* Save off the frame's lineno, and use the forced one, if provided. */ + orig_lineno = frame->f_lineno; + if (lineno > 0) { + frame->f_lineno = lineno; + } + /* Invoke the C function, and return ourselves. */ if (Tracer_trace(self, frame, what, arg) == RET_OK) { Py_INCREF(self); - return (PyObject *)self; + ret = (PyObject *)self; } + /* Clean up. */ + frame->f_lineno = orig_lineno; + done: - return NULL; + return ret; } static PyObject * |