Skip to content

Commit a200fb5

Browse files
committed
fix: adapt to a 3.13 change in frame.f_lasti
During a yield from a generator, frame.f_lasti used to point to the YIELD bytecode. In 3.13, it now points to the RESUME that follows it. python/cpython#113728
1 parent 79a1221 commit a200fb5

File tree

4 files changed

+21
-4
lines changed

4 files changed

+21
-4
lines changed

coverage/ctracer/tracer.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,10 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame)
710710
real_return = TRUE;
711711
}
712712
else {
713-
real_return = (code_bytes[lasti + 2] != RESUME);
713+
#if ENV_LASTI_IS_YIELD
714+
lasti += 2;
715+
#endif
716+
real_return = (code_bytes[lasti] != RESUME);
714717
}
715718
#else
716719
/* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read

coverage/ctracer/util.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
#define MyCode_FreeCode(code)
6060
#endif
6161

62+
// Where does frame.f_lasti point when yielding from a generator?
63+
// It used to point at the YIELD, now it points at the RESUME.
64+
// https://github.com/python/cpython/issues/113728
65+
#define ENV_LASTI_IS_YIELD (PY_VERSION_HEX < 0x030D0000)
66+
6267
/* The values returned to indicate ok or error. */
6368
#define RET_OK 0
6469
#define RET_ERROR -1

coverage/env.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ class PYBEHAVIOR:
117117
# PEP669 Low Impact Monitoring: https://peps.python.org/pep-0669/
118118
pep669 = bool(getattr(sys, "monitoring", None))
119119

120+
# Where does frame.f_lasti point when yielding from a generator?
121+
# It used to point at the YIELD, now it points at the RESUME.
122+
# https://github.com/python/cpython/issues/113728
123+
lasti_is_yield = (PYVERSION < (3, 13))
124+
120125

121126
# Coverage.py specifics, about testing scenarios. See tests/testenv.py also.
122127

coverage/pytracer.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222

2323
# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
24+
# PYVERSIONS: RESUME is new in Python3.11
2425
RESUME = dis.opmap.get("RESUME")
2526
RETURN_VALUE = dis.opmap["RETURN_VALUE"]
2627
if RESUME is None:
@@ -137,7 +138,8 @@ def _trace(
137138
if THIS_FILE in frame.f_code.co_filename:
138139
return None
139140

140-
#self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
141+
# f = frame; code = f.f_code
142+
# self.log(":", f"{code.co_filename} {f.f_lineno} {code.co_name}()", event)
141143

142144
if (self.stopped and sys.gettrace() == self._cached_bound_method_trace): # pylint: disable=comparison-with-callable
143145
# The PyTrace.stop() method has been called, possibly by another
@@ -255,8 +257,10 @@ def _trace(
255257
# A return from the end of a code object is a real return.
256258
real_return = True
257259
else:
258-
# it's a real return.
259-
real_return = (code[lasti + 2] != RESUME)
260+
# It is a real return if we aren't going to resume next.
261+
if env.PYBEHAVIOR.lasti_is_yield:
262+
lasti += 2
263+
real_return = (code[lasti] != RESUME)
260264
else:
261265
if code[lasti] == RETURN_VALUE:
262266
real_return = True

0 commit comments

Comments
 (0)