diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2010-08-26 08:12:29 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2010-08-26 08:12:29 -0400 |
commit | aa75e73d926b6350da8336a46809a4406e187edd (patch) | |
tree | 0943aab07a762bc004dc8998af48c603daf55bbc /coverage | |
parent | 7c14b919bae2ea0e4c72e5bb4116626f3a9b6230 (diff) | |
download | python-coveragepy-aa75e73d926b6350da8336a46809a4406e187edd.tar.gz |
The thread-startup dance caused Thread.run() to not be measured. This fixes it, I hope without introducing too much more new code. Fixes #85.
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/collector.py | 20 | ||||
-rw-r--r-- | coverage/tracer.c | 56 |
2 files changed, 65 insertions, 11 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index 55211f2..d20e42c 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -96,8 +96,13 @@ class PyTracer(object): return self._trace def start(self): - """Start this Tracer.""" + """Start this Tracer. + + Return a Python function suitable for use with sys.settrace(). + + """ sys.settrace(self._trace) + return self._trace def stop(self): """Stop this Tracer.""" @@ -186,8 +191,9 @@ class Collector(object): tracer.arcs = self.branch tracer.should_trace = self.should_trace tracer.should_trace_cache = self.should_trace_cache - tracer.start() + fn = tracer.start() self.tracers.append(tracer) + return fn # The trace function has to be set individually on each thread before # execution begins. Ironically, the only support the threading module has @@ -200,9 +206,13 @@ class Collector(object): # Remove ourselves as the trace function sys.settrace(None) # Install the real tracer. - self._start_tracer() - # Return None to reiterate that we shouldn't be used for tracing. - return None + fn = self._start_tracer() + # Invoke the real trace function with the current event, to be sure + # not to lose an event. + if fn: + fn = fn(frame_unused, event_unused, arg_unused) + # Return the new trace function to continue tracing in this scope. + return fn def start(self): """Start collecting trace information.""" diff --git a/coverage/tracer.c b/coverage/tracer.c index 1d22729..583ed80 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -25,14 +25,16 @@ #if PY_MAJOR_VERSION >= 3
+#define MyText_Type PyUnicode_Type
#define MyText_Check(o) PyUnicode_Check(o)
-#define MyText_AS_STRING(o) FOOEY_DONT_KNOW_YET(o)
+#define MyText_AS_STRING(o) PyBytes_AS_STRING(PyUnicode_AsASCIIString(o))
#define MyInt_FromLong(l) PyLong_FromLong(l)
#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
#else
+#define MyText_Type PyString_Type
#define MyText_Check(o) PyString_Check(o)
#define MyText_AS_STRING(o) PyString_AS_STRING(o)
#define MyInt_FromLong(l) PyInt_FromLong(l)
@@ -117,7 +119,7 @@ typedef struct { #define STACK_DELTA 100
static int
-Tracer_init(Tracer *self, PyObject *args, PyObject *kwds)
+Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused)
{
#if COLLECT_STATS
self->stats.calls = 0;
@@ -247,7 +249,7 @@ Tracer_record_pair(Tracer *self, int l1, int l2) * The Trace Function
*/
static int
-Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg)
+Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
{
int ret = RET_OK;
PyObject * filename = NULL;
@@ -449,19 +451,58 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg) return RET_OK;
}
+/*
+ * A sys.settrace-compatible function that invokes our C trace function.
+ */
static PyObject *
-Tracer_start(Tracer *self, PyObject *args)
+Tracer_pytrace(Tracer *self, PyObject *args)
+{
+ PyFrameObject *frame;
+ PyObject *what_str;
+ PyObject *arg_unused;
+ int what;
+ static char *what_names[] = {
+ "call", "exception", "line", "return",
+ "c_call", "c_exception", "c_return",
+ NULL
+ };
+
+ if (!PyArg_ParseTuple(args, "O!O!O:Tracer_pytrace",
+ &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg_unused)) {
+ goto done;
+ }
+
+ /* In Python, the what argument is a string, we need to find an int
+ for the C function. */
+ for (what = 0; what_names[what]; what++) {
+ if (!strcmp(MyText_AS_STRING(what_str), what_names[what])) {
+ break;
+ }
+ }
+
+ /* Invoke the C function, and return ourselves. */
+ if (Tracer_trace(self, frame, what, arg_unused) == RET_OK) {
+ return PyObject_GetAttrString((PyObject*)self, "pytrace");
+ }
+
+done:
+ return NULL;
+}
+
+static PyObject *
+Tracer_start(Tracer *self, PyObject *args_unused)
{
PyEval_SetTrace((Py_tracefunc)Tracer_trace, (PyObject*)self);
self->started = 1;
self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs);
self->last_line = -1;
- return Py_BuildValue("");
+ /* start() returns a trace function usable with sys.settrace() */
+ return PyObject_GetAttrString((PyObject*)self, "pytrace");
}
static PyObject *
-Tracer_stop(Tracer *self, PyObject *args)
+Tracer_stop(Tracer *self, PyObject *args_unused)
{
if (self->started) {
PyEval_SetTrace(NULL, NULL);
@@ -512,6 +553,9 @@ Tracer_members[] = { static PyMethodDef
Tracer_methods[] = {
+ { "pytrace", (PyCFunction) Tracer_pytrace, METH_VARARGS,
+ PyDoc_STR("A trace function compatible with sys.settrace()") },
+
{ "start", (PyCFunction) Tracer_start, METH_VARARGS,
PyDoc_STR("Start the tracer") },
|