diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2015-02-28 13:11:39 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2015-02-28 13:11:39 -0500 |
commit | 66f8ab7588d2517d04bc1f2d2157543a1e7185b6 (patch) | |
tree | 377feff27b4bc79fcc6016d82b73985c030ba36c | |
parent | b8a9b940ab9a5e9a7f12c7bb8c4dabed846acc3f (diff) | |
download | python-coveragepy-git-66f8ab7588d2517d04bc1f2d2157543a1e7185b6.tar.gz |
Finish the plugin error checking in CTracer.
-rw-r--r-- | coverage/tracer.c | 59 | ||||
-rw-r--r-- | tests/test_plugins.py | 51 |
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) |