eric7/DebugClients/Python/coverage/pytracer.py

branch
eric7
changeset 8991
2fc945191992
parent 8775
0802ae193343
diff -r ca8e477c590c -r 2fc945191992 eric7/DebugClients/Python/coverage/pytracer.py
--- a/eric7/DebugClients/Python/coverage/pytracer.py	Sun Mar 20 17:26:35 2022 +0100
+++ b/eric7/DebugClients/Python/coverage/pytracer.py	Sun Mar 20 17:49:44 2022 +0100
@@ -10,14 +10,18 @@
 from coverage import env
 
 # We need the YIELD_VALUE opcode below, in a comparison-friendly form.
-YIELD_VALUE = dis.opmap['YIELD_VALUE']
+RESUME = dis.opmap.get('RESUME')
+RETURN_VALUE = dis.opmap['RETURN_VALUE']
+if RESUME is None:
+    YIELD_VALUE = dis.opmap['YIELD_VALUE']
+    YIELD_FROM = dis.opmap['YIELD_FROM']
+    YIELD_FROM_OFFSET = 0 if env.PYPY else 2
 
 # When running meta-coverage, this file can try to trace itself, which confuses
 # everything.  Don't trace ourselves.
 
 THIS_FILE = __file__.rstrip("co")
 
-
 class PyTracer:
     """Python implementation of the raw data tracer."""
 
@@ -64,7 +68,7 @@
         atexit.register(setattr, self, 'in_atexit', True)
 
     def __repr__(self):
-        return "<PyTracer at {}: {} lines in {} files>".format(
+        return "<PyTracer at 0x{:x}: {} lines in {} files>".format(
             id(self),
             sum(len(v) for v in self.data.values()),
             len(self.data),
@@ -78,13 +82,13 @@
                 id(self),
                 len(self.data_stack),
             ))
-            if 0:
+            if 0:   # if you want thread ids..
                 f.write(".{:x}.{:x}".format(
                     self.thread.ident,
                     self.threading.current_thread().ident,
                 ))
             f.write(" {}".format(" ".join(map(str, args))))
-            if 0:
+            if 0:   # if you want callers..
                 f.write(" | ")
                 stack = " / ".join(
                     (fname or "???").rpartition("/")[-1]
@@ -132,8 +136,7 @@
             else:
                 self.started_context = False
 
-            # Entering a new frame.  Decide if we should trace
-            # in this file.
+            # Entering a new frame.  Decide if we should trace in this file.
             self._activity = True
             self.data_stack.append(
                 (
@@ -160,7 +163,14 @@
             # function calls and re-entering generators.  The f_lasti field is
             # -1 for calls, and a real offset for generators.  Use <0 as the
             # line number for calls, and the real line number for generators.
-            if getattr(frame, 'f_lasti', -1) < 0:
+            if RESUME is not None:
+                # The current opcode is guaranteed to be RESUME. The argument
+                # determines what kind of resume it is.
+                oparg = frame.f_code.co_code[frame.f_lasti + 1]
+                real_call = (oparg == 0)
+            else:
+                real_call = (getattr(frame, 'f_lasti', -1) < 0)
+            if real_call:
                 self.last_line = -frame.f_code.co_firstlineno
             else:
                 self.last_line = frame.f_lineno
@@ -178,9 +188,27 @@
             if self.trace_arcs and self.cur_file_data:
                 # Record an arc leaving the function, but beware that a
                 # "return" event might just mean yielding from a generator.
-                # Jython seems to have an empty co_code, so just assume return.
                 code = frame.f_code.co_code
-                if (not code) or code[frame.f_lasti] != YIELD_VALUE:
+                lasti = frame.f_lasti
+                if RESUME is not None:
+                    if len(code) == lasti + 2:
+                        # A return from the end of a code object is a real return.
+                        real_return = True
+                    else:
+                        # it's a real return.
+                        real_return = (code[lasti + 2] != RESUME)
+                else:
+                    if code[lasti] == RETURN_VALUE:
+                        real_return = True
+                    elif code[lasti] == YIELD_VALUE:
+                        real_return = False
+                    elif len(code) <= lasti + YIELD_FROM_OFFSET:
+                        real_return = True
+                    elif code[lasti + YIELD_FROM_OFFSET] == YIELD_FROM:
+                        real_return = False
+                    else:
+                        real_return = True
+                if real_return:
                     first = frame.f_code.co_firstlineno
                     self.cur_file_data.add((self.last_line, -first))
             # Leaving this function, pop the filename stack.
@@ -238,8 +266,10 @@
             # has changed to None.
             dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
             if (not dont_warn) and tf != self._trace:   # pylint: disable=comparison-with-callable
-                msg = f"Trace function changed, measurement is likely wrong: {tf!r}"
-                self.warn(msg, slug="trace-changed")
+                self.warn(
+                    f"Trace function changed, data is likely wrong: {tf!r} != {self._trace!r}",
+                    slug="trace-changed",
+                )
 
     def activity(self):
         """Has there been any activity?"""

eric ide

mercurial