From ab48a7bf66c04754e9964587b2b4f790bb6af8d4 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 8 Jul 2021 14:00:40 -0400 Subject: [PATCH 1/2] refactor: Python 3.9 added an accessor for frame->f_code This accessor is now required in 3.11, so let's use it. --- coverage/ctracer/tracer.c | 22 +++++++++++----------- coverage/ctracer/util.h | 7 +++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c index d90f1bc3b..d332173cd 100644 --- a/coverage/ctracer/tracer.c +++ b/coverage/ctracer/tracer.c @@ -305,7 +305,7 @@ CTracer_check_missing_return(CTracer *self, PyFrameObject *frame) goto error; } } - SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "missedreturn"); + SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "missedreturn"); self->pdata_stack->depth--; self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; } @@ -384,7 +384,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame) } /* Check if we should trace this line. */ - filename = frame->f_code->co_filename; + filename = MyFrame_GetCode(frame)->co_filename; disposition = PyDict_GetItem(self->should_trace_cache, filename); if (disposition == NULL) { if (PyErr_Occurred()) { @@ -549,7 +549,7 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame) * real byte offset for a generator re-entry. */ if (frame->f_lasti < 0) { - self->pcur_entry->last_line = -frame->f_code->co_firstlineno; + self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno; } else { self->pcur_entry->last_line = PyFrame_GetLineNumber(frame); @@ -633,7 +633,7 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame) STATS( self->stats.lines++; ) if (self->pdata_stack->depth >= 0) { - SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "line"); + SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "line"); if (self->pcur_entry->file_data) { int lineno_from = -1; int lineno_to = -1; @@ -714,14 +714,14 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame) * f_lasti before reading the byte. */ int bytecode = RETURN_VALUE; - PyObject * pCode = frame->f_code->co_code; + PyObject * pCode = MyFrame_GetCode(frame)->co_code; int lasti = MyFrame_lasti(frame); if (lasti < PyBytes_GET_SIZE(pCode)) { bytecode = PyBytes_AS_STRING(pCode)[lasti]; } if (bytecode != YIELD_VALUE) { - int first = frame->f_code->co_firstlineno; + int first = MyFrame_GetCode(frame)->co_firstlineno; if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) { goto error; } @@ -744,7 +744,7 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame) } /* Pop the stack. */ - SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "return"); + SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "return"); self->pdata_stack->depth--; self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; } @@ -775,7 +775,7 @@ CTracer_handle_exception(CTracer *self, PyFrameObject *frame) */ STATS( self->stats.exceptions++; ) self->last_exc_back = frame->f_back; - self->last_exc_firstlineno = frame->f_code->co_firstlineno; + self->last_exc_firstlineno = MyFrame_GetCode(frame)->co_firstlineno; return RET_OK; } @@ -806,14 +806,14 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse #if WHAT_LOG if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) { - ascii = PyUnicode_AsASCIIString(frame->f_code->co_filename); + ascii = PyUnicode_AsASCIIString(MyFrame_GetCode(frame)->co_filename); printf("trace: %s @ %s %d\n", what_sym[what], PyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame)); Py_DECREF(ascii); } #endif #if TRACE_LOG - ascii = PyUnicode_AsASCIIString(frame->f_code->co_filename); + ascii = PyUnicode_AsASCIIString(MyFrame_GetCode(frame)->co_filename); if (strstr(PyBytes_AS_STRING(ascii), start_file) && PyFrame_GetLineNumber(frame) == start_line) { logging = TRUE; } @@ -930,7 +930,7 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) } #if WHAT_LOG - ascii = PyUnicode_AsASCIIString(frame->f_code->co_filename); + ascii = PyUnicode_AsASCIIString(MyFrame_GetCode(frame)->co_filename); printf("pytrace: %s @ %s %d\n", what_sym[what], PyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame)); Py_DECREF(ascii); #endif diff --git a/coverage/ctracer/util.h b/coverage/ctracer/util.h index 67b0fa756..a0b0e236e 100644 --- a/coverage/ctracer/util.h +++ b/coverage/ctracer/util.h @@ -20,6 +20,13 @@ #define MyFrame_lasti(f) f->f_lasti #endif // 3.10.0a7 +// Access f_code should be done through a helper starting in 3.9. +#if PY_VERSION_HEX >= 0x03090000 +#define MyFrame_GetCode(f) (PyFrame_GetCode(f)) +#else +#define MyFrame_GetCode(f) ((f)->f_code) +#endif // 3.11 + /* The values returned to indicate ok or error. */ #define RET_OK 0 #define RET_ERROR -1 From 809cccb626cead091fdbf187e85322a6fe156ad9 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 8 Jul 2021 14:05:48 -0400 Subject: [PATCH 2/2] test: add a test for #1184. Note: this test fails on 3.10.0b3, the current 3.10 version in the CI tests. --- tests/test_arcs.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 22446f6a1..db7562916 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -161,6 +161,23 @@ def test_what_is_the_sound_of_no_lines_clapping(self): arcz_missing=arcz_missing, ) + def test_bug_1184(self): + self.check_coverage("""\ + def foo(x): + if x: + try: + 1/(x - 1) + except ZeroDivisionError: + pass + return x # 7 + + for i in range(3): # 9 + foo(i) + """, + arcz=".1 19 9-1 .2 23 27 34 47 56 67 7-1 9A A9", + arcz_unpredicted="45", + ) + class WithTest(CoverageTest): """Arc-measuring tests involving context managers."""