eric6/DebugClients/Python/coverage/pytracer.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
child 7975
7d493839a8fc
equal deleted inserted replaced
7426:dc171b1d8261 7427:362cd1b6f81a
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3 3
4 """Raw data collector for coverage.py.""" 4 """Raw data collector for coverage.py."""
5 5
6 import atexit 6 import atexit
7 import dis 7 import dis
38 # Attributes set from the collector: 38 # Attributes set from the collector:
39 self.data = None 39 self.data = None
40 self.trace_arcs = False 40 self.trace_arcs = False
41 self.should_trace = None 41 self.should_trace = None
42 self.should_trace_cache = None 42 self.should_trace_cache = None
43 self.should_start_context = None
43 self.warn = None 44 self.warn = None
44 # The threading module to use, if any. 45 # The threading module to use, if any.
45 self.threading = None 46 self.threading = None
46 47
47 self.cur_file_dict = None 48 self.cur_file_dict = None
48 self.last_line = 0 # int, but uninitialized. 49 self.last_line = 0 # int, but uninitialized.
49 self.cur_file_name = None 50 self.cur_file_name = None
51 self.context = None
52 self.started_context = False
50 53
51 self.data_stack = [] 54 self.data_stack = []
52 self.last_exc_back = None 55 self.last_exc_back = None
53 self.last_exc_firstlineno = 0 56 self.last_exc_firstlineno = 0
54 self.thread = None 57 self.thread = None
58 self.in_atexit = False 61 self.in_atexit = False
59 # On exit, self.in_atexit = True 62 # On exit, self.in_atexit = True
60 atexit.register(setattr, self, 'in_atexit', True) 63 atexit.register(setattr, self, 'in_atexit', True)
61 64
62 def __repr__(self): 65 def __repr__(self):
63 return "<PyTracer at {0}: {1} lines in {2} files>".format( 66 return "<PyTracer at {}: {} lines in {} files>".format(
64 id(self), 67 id(self),
65 sum(len(v) for v in self.data.values()), 68 sum(len(v) for v in self.data.values()),
66 len(self.data), 69 len(self.data),
67 ) 70 )
68 71
81 def _trace(self, frame, event, arg_unused): 84 def _trace(self, frame, event, arg_unused):
82 """The trace function passed to sys.settrace.""" 85 """The trace function passed to sys.settrace."""
83 86
84 #self.log(":", frame.f_code.co_filename, frame.f_lineno, event) 87 #self.log(":", frame.f_code.co_filename, frame.f_lineno, event)
85 88
86 if (self.stopped and sys.gettrace() == self._trace): 89 if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable
87 # The PyTrace.stop() method has been called, possibly by another 90 # The PyTrace.stop() method has been called, possibly by another
88 # thread, let's deactivate ourselves now. 91 # thread, let's deactivate ourselves now.
89 #self.log("X", frame.f_code.co_filename, frame.f_lineno) 92 #self.log("X", frame.f_code.co_filename, frame.f_lineno)
90 sys.settrace(None) 93 sys.settrace(None)
91 return None 94 return None
94 if frame == self.last_exc_back: 97 if frame == self.last_exc_back:
95 # Someone forgot a return event. 98 # Someone forgot a return event.
96 if self.trace_arcs and self.cur_file_dict: 99 if self.trace_arcs and self.cur_file_dict:
97 pair = (self.last_line, -self.last_exc_firstlineno) 100 pair = (self.last_line, -self.last_exc_firstlineno)
98 self.cur_file_dict[pair] = None 101 self.cur_file_dict[pair] = None
99 self.cur_file_dict, self.cur_file_name, self.last_line = self.data_stack.pop() 102 self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
103 self.data_stack.pop()
104 )
100 self.last_exc_back = None 105 self.last_exc_back = None
101 106
102 if event == 'call': 107 if event == 'call':
103 # Entering a new function context. Decide if we should trace 108 # Should we start a new context?
109 if self.should_start_context and self.context is None:
110 context_maybe = self.should_start_context(frame)
111 if context_maybe is not None:
112 self.context = context_maybe
113 self.started_context = True
114 self.switch_context(self.context)
115 else:
116 self.started_context = False
117 else:
118 self.started_context = False
119
120 # Entering a new frame. Decide if we should trace
104 # in this file. 121 # in this file.
105 self._activity = True 122 self._activity = True
106 self.data_stack.append((self.cur_file_dict, self.cur_file_name, self.last_line)) 123 self.data_stack.append(
124 (
125 self.cur_file_dict,
126 self.cur_file_name,
127 self.last_line,
128 self.started_context,
129 )
130 )
107 filename = frame.f_code.co_filename 131 filename = frame.f_code.co_filename
108 self.cur_file_name = filename 132 self.cur_file_name = filename
109 disp = self.should_trace_cache.get(filename) 133 disp = self.should_trace_cache.get(filename)
110 if disp is None: 134 if disp is None:
111 disp = self.should_trace(filename, frame) 135 disp = self.should_trace(filename, frame)
144 code = frame.f_code.co_code 168 code = frame.f_code.co_code
145 if (not code) or code[frame.f_lasti] != YIELD_VALUE: 169 if (not code) or code[frame.f_lasti] != YIELD_VALUE:
146 first = frame.f_code.co_firstlineno 170 first = frame.f_code.co_firstlineno
147 self.cur_file_dict[(self.last_line, -first)] = None 171 self.cur_file_dict[(self.last_line, -first)] = None
148 # Leaving this function, pop the filename stack. 172 # Leaving this function, pop the filename stack.
149 self.cur_file_dict, self.cur_file_name, self.last_line = self.data_stack.pop() 173 self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
174 self.data_stack.pop()
175 )
176 # Leaving a context?
177 if self.started_context:
178 self.context = None
179 self.switch_context(None)
150 elif event == 'exception': 180 elif event == 'exception':
151 self.last_exc_back = frame.f_back 181 self.last_exc_back = frame.f_back
152 self.last_exc_firstlineno = frame.f_code.co_firstlineno 182 self.last_exc_firstlineno = frame.f_code.co_firstlineno
153 return self._trace 183 return self._trace
154 184
173 sys.settrace(self._trace) 203 sys.settrace(self._trace)
174 return self._trace 204 return self._trace
175 205
176 def stop(self): 206 def stop(self):
177 """Stop this Tracer.""" 207 """Stop this Tracer."""
178 # Get the activate tracer callback before setting the stop flag to be 208 # Get the active tracer callback before setting the stop flag to be
179 # able to detect if the tracer was changed prior to stopping it. 209 # able to detect if the tracer was changed prior to stopping it.
180 tf = sys.gettrace() 210 tf = sys.gettrace()
181 211
182 # Set the stop flag. The actual call to sys.settrace(None) will happen 212 # Set the stop flag. The actual call to sys.settrace(None) will happen
183 # in the self._trace callback itself to make sure to call it from the 213 # in the self._trace callback itself to make sure to call it from the
194 if self.warn: 224 if self.warn:
195 # PyPy clears the trace function before running atexit functions, 225 # PyPy clears the trace function before running atexit functions,
196 # so don't warn if we are in atexit on PyPy and the trace function 226 # so don't warn if we are in atexit on PyPy and the trace function
197 # has changed to None. 227 # has changed to None.
198 dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None) 228 dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
199 if (not dont_warn) and tf != self._trace: 229 if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable
200 self.warn( 230 self.warn(
201 "Trace function changed, measurement is likely wrong: %r" % (tf,), 231 "Trace function changed, measurement is likely wrong: %r" % (tf,),
202 slug="trace-changed", 232 slug="trace-changed",
203 ) 233 )
204 234

eric ide

mercurial