summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2015-02-28 13:11:39 -0500
committerNed Batchelder <ned@nedbatchelder.com>2015-02-28 13:11:39 -0500
commit66f8ab7588d2517d04bc1f2d2157543a1e7185b6 (patch)
tree377feff27b4bc79fcc6016d82b73985c030ba36c
parentb8a9b940ab9a5e9a7f12c7bb8c4dabed846acc3f (diff)
downloadpython-coveragepy-git-66f8ab7588d2517d04bc1f2d2157543a1e7185b6.tar.gz
Finish the plugin error checking in CTracer.
-rw-r--r--coverage/tracer.c59
-rw-r--r--tests/test_plugins.py51
2 files changed, 101 insertions, 9 deletions
diff --git a/coverage/tracer.c b/coverage/tracer.c
index 5ff7ea1b..11b5ce7b 100644
--- a/coverage/tracer.c
+++ b/coverage/tracer.c
@@ -47,14 +47,17 @@
#define RET_OK 0
#define RET_ERROR -1
-/* An entry on the data stack. For each call frame, we need to record the
- dictionary to capture data, and the last line number executed in that
- frame.
-*/
+/* An entry on the data stack. For each call frame, we need to record all
+ * the information needed for CTracer_handle_line to operate as quickly as
+ * possible.
+ */
typedef struct {
/* The current file_data dictionary. Borrowed, owned by self->data. */
PyObject * file_data;
+ /* The disposition object for this frame. */
+ PyObject * disposition;
+
/* The FileTracer handling this frame, or None if it's Python. */
PyObject * file_tracer;
@@ -581,6 +584,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
SHOWLOG(self->pdata_stack->depth, frame->f_lineno, filename, "skipped");
}
+ self->cur_entry.disposition = disposition;
self->cur_entry.last_line = -1;
ok:
@@ -665,9 +669,43 @@ ok:
static int
+CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
+{
+ int ret = RET_ERROR;
+ int the_int;
+ PyObject * pyint = NULL;
+
+ if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "line_number_range must return 2-tuple"
+ );
+ goto error;
+ }
+
+ for (int index = 0; index < 2; index++) {
+ pyint = PyTuple_GetItem(pair, index);
+ if (pyint == NULL) {
+ goto error;
+ }
+ the_int = MyInt_AsInt(pyint);
+ if (the_int == -1 && PyErr_Occurred()) {
+ goto error;
+ }
+ *(index == 0 ? p_one : p_two) = the_int;
+ }
+
+ ret = RET_OK;
+
+error:
+ return ret;
+}
+
+static int
CTracer_handle_line(CTracer *self, PyFrameObject *frame)
{
int ret = RET_ERROR;
+ int ret2;
STATS( self->stats.lines++; )
if (self->pdata_stack->depth >= 0) {
@@ -682,10 +720,12 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
if (from_to == NULL) {
goto error;
}
- /* TODO: error check bad returns. */
- lineno_from = MyInt_AsInt(PyTuple_GetItem(from_to, 0));
- lineno_to = MyInt_AsInt(PyTuple_GetItem(from_to, 1));
+ ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
Py_DECREF(from_to);
+ if (ret2 < 0) {
+ CTracer_disable_plugin(self, self->cur_entry.disposition);
+ goto ok;
+ }
}
else {
lineno_from = lineno_to = frame->f_lineno;
@@ -706,9 +746,9 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
goto error;
}
- ret = PyDict_SetItem(self->cur_entry.file_data, this_line, Py_None);
+ ret2 = PyDict_SetItem(self->cur_entry.file_data, this_line, Py_None);
Py_DECREF(this_line);
- if (ret < 0) {
+ if (ret2 < 0) {
goto error;
}
}
@@ -719,6 +759,7 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
}
}
+ok:
ret = RET_OK;
error:
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index c8272514..69e7b42b 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -557,3 +557,54 @@ class BadPluginTest(FileTracerTest):
101/0 # Oh noes!
""")
self.run_bad_plugin("bad_plugin")
+
+ def test_line_number_range_returns_non_tuple(self):
+ self.make_file("bad_plugin.py", """\
+ import coverage.plugin
+ class Plugin(coverage.plugin.CoveragePlugin):
+ def file_tracer(self, filename):
+ if filename.endswith("other.py"):
+ return BadFileTracer()
+
+ class BadFileTracer(coverage.plugin.FileTracer):
+ def source_filename(self):
+ return "something.foo"
+
+ def line_number_range(self, frame):
+ return 42.23
+ """)
+ self.run_bad_plugin("bad_plugin", our_error=False)
+
+ def test_line_number_range_returns_triple(self):
+ self.make_file("bad_plugin.py", """\
+ import coverage.plugin
+ class Plugin(coverage.plugin.CoveragePlugin):
+ def file_tracer(self, filename):
+ if filename.endswith("other.py"):
+ return BadFileTracer()
+
+ class BadFileTracer(coverage.plugin.FileTracer):
+ def source_filename(self):
+ return "something.foo"
+
+ def line_number_range(self, frame):
+ return (1, 2, 3)
+ """)
+ self.run_bad_plugin("bad_plugin", our_error=False)
+
+ def test_line_number_range_returns_pair_of_strings(self):
+ self.make_file("bad_plugin.py", """\
+ import coverage.plugin
+ class Plugin(coverage.plugin.CoveragePlugin):
+ def file_tracer(self, filename):
+ if filename.endswith("other.py"):
+ return BadFileTracer()
+
+ class BadFileTracer(coverage.plugin.FileTracer):
+ def source_filename(self):
+ return "something.foo"
+
+ def line_number_range(self, frame):
+ return ("5", "7")
+ """)
+ self.run_bad_plugin("bad_plugin", our_error=False)