summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/collector.py7
-rw-r--r--coverage/fullcoverage/encodings.py15
-rw-r--r--coverage/tracer.c33
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 *