src/eric7/DebugClients/Python/coverage/pytracer.py

branch
eric7
changeset 9252
32dd11232e06
parent 9209
b99e7fd55fd3
--- a/src/eric7/DebugClients/Python/coverage/pytracer.py	Wed Jul 20 16:03:55 2022 +0200
+++ b/src/eric7/DebugClients/Python/coverage/pytracer.py	Wed Jul 20 16:13:29 2022 +0200
@@ -67,6 +67,10 @@
         # On exit, self.in_atexit = True
         atexit.register(setattr, self, 'in_atexit', True)
 
+        # Cache a bound method on the instance, so that we don't have to
+        # re-create a bound method object all the time.
+        self._cached_bound_method_trace = self._trace
+
     def __repr__(self):
         return "<PyTracer at 0x{:x}: {} lines in {} files>".format(
             id(self),
@@ -105,7 +109,7 @@
 
         #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
 
-        if (self.stopped and sys.gettrace() == self._trace):    # pylint: disable=comparison-with-callable
+        if (self.stopped and sys.gettrace() == self._cached_bound_method_trace):    # pylint: disable=comparison-with-callable
             # The PyTrace.stop() method has been called, possibly by another
             # thread, let's deactivate ourselves now.
             if 0:
@@ -129,12 +133,13 @@
                 context_maybe = self.should_start_context(frame)
                 if context_maybe is not None:
                     self.context = context_maybe
-                    self.started_context = True
+                    started_context = True
                     self.switch_context(self.context)
                 else:
-                    self.started_context = False
+                    started_context = False
             else:
-                self.started_context = False
+                started_context = False
+            self.started_context = started_context
 
             # Entering a new frame.  Decide if we should trace in this file.
             self._activity = True
@@ -143,22 +148,35 @@
                     self.cur_file_data,
                     self.cur_file_name,
                     self.last_line,
-                    self.started_context,
+                    started_context,
                 )
             )
-            filename = frame.f_code.co_filename
-            self.cur_file_name = filename
-            disp = self.should_trace_cache.get(filename)
-            if disp is None:
-                disp = self.should_trace(filename, frame)
-                self.should_trace_cache[filename] = disp
 
-            self.cur_file_data = None
-            if disp.trace:
-                tracename = disp.source_filename
-                if tracename not in self.data:
-                    self.data[tracename] = set()
-                self.cur_file_data = self.data[tracename]
+            # Improve tracing performance: when calling a function, both caller
+            # and callee are often within the same file. if that's the case, we
+            # don't have to re-check whether to trace the corresponding
+            # function (which is a little bit espensive since it involves
+            # dictionary lookups). This optimization is only correct if we
+            # didn't start a context.
+            filename = frame.f_code.co_filename
+            if filename != self.cur_file_name or started_context:
+                self.cur_file_name = filename
+                disp = self.should_trace_cache.get(filename)
+                if disp is None:
+                    disp = self.should_trace(filename, frame)
+                    self.should_trace_cache[filename] = disp
+
+                self.cur_file_data = None
+                if disp.trace:
+                    tracename = disp.source_filename
+                    if tracename not in self.data:
+                        self.data[tracename] = set()
+                    self.cur_file_data = self.data[tracename]
+                else:
+                    frame.f_trace_lines = False
+            elif not self.cur_file_data:
+                frame.f_trace_lines = False
+
             # The call event is really a "start frame" event, and happens for
             # function calls and re-entering generators.  The f_lasti field is
             # -1 for calls, and a real offset for generators.  Use <0 as the
@@ -174,6 +192,7 @@
                 self.last_line = -frame.f_code.co_firstlineno
             else:
                 self.last_line = frame.f_lineno
+
         elif event == 'line':
             # Record an executed line.
             if self.cur_file_data is not None:
@@ -184,6 +203,7 @@
                 else:
                     self.cur_file_data.add(lineno)
                 self.last_line = lineno
+
         elif event == 'return':
             if self.trace_arcs and self.cur_file_data:
                 # Record an arc leaving the function, but beware that a
@@ -211,6 +231,7 @@
                 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.
             self.cur_file_data, self.cur_file_name, self.last_line, self.started_context = (
                 self.data_stack.pop()
@@ -219,7 +240,7 @@
             if self.started_context:
                 self.context = None
                 self.switch_context(None)
-        return self._trace
+        return self._cached_bound_method_trace
 
     def start(self):
         """Start this Tracer.
@@ -237,10 +258,10 @@
                     # function, but we are marked as running again, so maybe it
                     # will be ok?
                     #self.log("~", "starting on different threads")
-                    return self._trace
+                    return self._cached_bound_method_trace
 
-        sys.settrace(self._trace)
-        return self._trace
+        sys.settrace(self._cached_bound_method_trace)
+        return self._cached_bound_method_trace
 
     def stop(self):
         """Stop this Tracer."""
@@ -265,9 +286,10 @@
             # so don't warn if we are in atexit on PyPy and the trace function
             # 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
+            if (not dont_warn) and tf != self._cached_bound_method_trace:   # pylint: disable=comparison-with-callable
                 self.warn(
-                    f"Trace function changed, data is likely wrong: {tf!r} != {self._trace!r}",
+                    "Trace function changed, data is likely wrong: " +
+                    f"{tf!r} != {self._cached_bound_method_trace!r}",
                     slug="trace-changed",
                 )
 

eric ide

mercurial