blob: 05faaeb77ff2b411f9395736c0dd489b9be89a16 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001from django_debug import is_django_render_call, get_template_file_name, get_template_line, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
2from django_debug import find_django_render_frame
3from django_frame import just_raised
4from django_frame import is_django_exception_break_context
5from django_frame import DjangoTemplateFrame
6from pydevd_comm import * #@UnusedWildImport
7from pydevd_breakpoints import * #@UnusedWildImport
8import traceback #@Reimport
9import os.path
10import sys
11import pydev_log
12from pydevd_signature import sendSignatureCallTrace
13
14basename = os.path.basename
15
16#=======================================================================================================================
17# PyDBFrame
18#=======================================================================================================================
19class PyDBFrame:
20 '''This makes the tracing for a given frame, so, the trace_dispatch
21 is used initially when we enter into a new context ('call') and then
22 is reused for the entire context.
23 '''
24
25 def __init__(self, args):
26 #args = mainDebugger, filename, base, info, t, frame
27 #yeap, much faster than putting in self and then getting it from self later on
28 self._args = args[:-1]
29
30 def setSuspend(self, *args, **kwargs):
31 self._args[0].setSuspend(*args, **kwargs)
32
33 def doWaitSuspend(self, *args, **kwargs):
34 self._args[0].doWaitSuspend(*args, **kwargs)
35
36 def trace_exception(self, frame, event, arg):
37 if event == 'exception':
38 (flag, frame) = self.shouldStopOnException(frame, event, arg)
39
40 if flag:
41 self.handle_exception(frame, event, arg)
42 return self.trace_dispatch
43
44 return self.trace_exception
45
46 def shouldStopOnException(self, frame, event, arg):
47 mainDebugger, filename, info, thread = self._args
48 flag = False
49
50 if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None:
51 (exception, value, trace) = arg
52
53 if trace is not None: #on jython trace is None on the first event
54 exception_breakpoint = get_exception_breakpoint(exception, dict(mainDebugger.exception_set), NOTIFY_ALWAYS)
55 if exception_breakpoint is not None:
56 if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
57 curr_func_name = frame.f_code.co_name
58 add_exception_to_frame(frame, (exception, value, trace))
59 self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK)
60 thread.additionalInfo.message = exception_breakpoint.qname
61 flag = True
62 else:
63 flag = False
64 else:
65 try:
66 if mainDebugger.django_exception_break and get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and just_raised(trace) and is_django_exception_break_context(frame):
67 render_frame = find_django_render_frame(frame)
68 if render_frame:
69 suspend_frame = suspend_django(self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
70
71 if suspend_frame:
72 add_exception_to_frame(suspend_frame, (exception, value, trace))
73 flag = True
74 thread.additionalInfo.message = 'VariableDoesNotExist'
75 suspend_frame.f_back = frame
76 frame = suspend_frame
77 except :
78 flag = False
79
80 return (flag, frame)
81
82 def handle_exception(self, frame, event, arg):
83 mainDebugger = self._args[0]
84 thread = self._args[3]
85 self.doWaitSuspend(thread, frame, event, arg)
86 mainDebugger.SetTraceForFrameAndParents(frame)
87
88 def trace_dispatch(self, frame, event, arg):
89 mainDebugger, filename, info, thread = self._args
90 try:
91 info.is_tracing = True
92
93 if mainDebugger._finishDebuggingSession:
94 return None
95
96 if getattr(thread, 'pydev_do_not_trace', None):
97 return None
98
99 if event == 'call':
100 sendSignatureCallTrace(mainDebugger, frame, filename)
101
102 if event not in ('line', 'call', 'return'):
103 if event == 'exception':
104 (flag, frame) = self.shouldStopOnException(frame, event, arg)
105 if flag:
106 self.handle_exception(frame, event, arg)
107 return self.trace_dispatch
108 else:
109 #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
110 return None
111
112 if event is not 'exception':
113 breakpoints_for_file = mainDebugger.breakpoints.get(filename)
114
115 can_skip = False
116
117 if info.pydev_state == STATE_RUN:
118 #we can skip if:
119 #- we have no stop marked
120 #- we should make a step return/step over and we're not in the current frame
121 can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\
122 or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame)
123
124 if mainDebugger.django_breakpoints:
125 can_skip = False
126
127 # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
128 # we will return nothing for the next trace
129 #also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
130 #so, that's why the additional checks are there.
131 if not breakpoints_for_file:
132 if can_skip:
133 if mainDebugger.always_exception_set or mainDebugger.django_exception_break:
134 return self.trace_exception
135 else:
136 return None
137
138 else:
139 #checks the breakpoint to see if there is a context match in some function
140 curr_func_name = frame.f_code.co_name
141
142 #global context is set with an empty name
143 if curr_func_name in ('?', '<module>'):
144 curr_func_name = ''
145
146 for breakpoint in breakpoints_for_file.values(): #jython does not support itervalues()
147 #will match either global or some function
148 if breakpoint.func_name in ('None', curr_func_name):
149 break
150
151 else: # if we had some break, it won't get here (so, that's a context that we want to skip)
152 if can_skip:
153 #print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd
154 return None
155 else:
156 breakpoints_for_file = None
157
158 #We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
159 #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
160
161 try:
162 line = frame.f_lineno
163
164
165 flag = False
166 if event == 'call' and info.pydev_state != STATE_SUSPEND and mainDebugger.django_breakpoints \
167 and is_django_render_call(frame):
168 (flag, frame) = self.shouldStopOnDjangoBreak(frame, event, arg)
169
170 #return is not taken into account for breakpoint hit because we'd have a double-hit in this case
171 #(one for the line and the other for the return).
172
173 if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\
174 and DictContains(breakpoints_for_file, line):
175 #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
176 # lets do the conditional stuff here
177 breakpoint = breakpoints_for_file[line]
178
179 stop = True
180 if info.pydev_step_cmd == CMD_STEP_OVER and info.pydev_step_stop is frame and event in ('line', 'return'):
181 stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
182 else:
183 if breakpoint.condition is not None:
184 try:
185 val = eval(breakpoint.condition, frame.f_globals, frame.f_locals)
186 if not val:
187 return self.trace_dispatch
188
189 except:
190 pydev_log.info('Error while evaluating condition \'%s\': %s\n' % (breakpoint.condition, sys.exc_info()[1]))
191
192 return self.trace_dispatch
193
194 if breakpoint.expression is not None:
195 try:
196 try:
197 val = eval(breakpoint.expression, frame.f_globals, frame.f_locals)
198 except:
199 val = sys.exc_info()[1]
200 finally:
201 if val is not None:
202 thread.additionalInfo.message = val
203
204 if stop:
205 self.setSuspend(thread, CMD_SET_BREAK)
206
207 # if thread has a suspend flag, we suspend with a busy wait
208 if info.pydev_state == STATE_SUSPEND:
209 self.doWaitSuspend(thread, frame, event, arg)
210 return self.trace_dispatch
211
212 except:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700213 traceback.print_exc()
Tor Norbye3a2425a2013-11-04 10:16:08 -0800214 raise
215
216 #step handling. We stop when we hit the right frame
217 try:
218 django_stop = False
219 if info.pydev_step_cmd == CMD_STEP_INTO:
220 stop = event in ('line', 'return')
221 if is_django_suspended(thread):
222 #django_stop = event == 'call' and is_django_render_call(frame)
223 stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame)
224 if stop:
225 info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame
226
227 elif info.pydev_step_cmd == CMD_STEP_OVER:
228 if is_django_suspended(thread):
229 django_stop = event == 'call' and is_django_render_call(frame)
230
231 stop = False
232 else:
233 if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back):
234 #we return to Django suspend mode and should not stop before django rendering frame
235 info.pydev_step_stop = info.pydev_django_resolve_frame
236 info.pydev_django_resolve_frame = None
237 thread.additionalInfo.suspend_type = DJANGO_SUSPEND
238
239
240 stop = info.pydev_step_stop is frame and event in ('line', 'return')
241
242 elif info.pydev_step_cmd == CMD_SMART_STEP_INTO:
243 stop = False
244 if info.pydev_smart_step_stop is frame:
245 info.pydev_func_name = None
246 info.pydev_smart_step_stop = None
247
248 if event == 'line' or event == 'exception':
249 curr_func_name = frame.f_code.co_name
250
251 #global context is set with an empty name
252 if curr_func_name in ('?', '<module>') or curr_func_name is None:
253 curr_func_name = ''
254
255 if curr_func_name == info.pydev_func_name:
256 stop = True
257
258 elif info.pydev_step_cmd == CMD_STEP_RETURN:
259 stop = event == 'return' and info.pydev_step_stop is frame
260
261 elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT:
262 stop = False
263
264 if event == 'line' or event == 'exception':
265 #Yes, we can only act on line events (weird hum?)
266 #Note: This code is duplicated at pydevd.py
267 #Acting on exception events after debugger breaks with exception
268 curr_func_name = frame.f_code.co_name
269
270 #global context is set with an empty name
271 if curr_func_name in ('?', '<module>'):
272 curr_func_name = ''
273
274 if curr_func_name == info.pydev_func_name:
275 line = info.pydev_next_line
276 if frame.f_lineno == line:
277 stop = True
278 else:
279 if frame.f_trace is None:
280 frame.f_trace = self.trace_dispatch
281 frame.f_lineno = line
282 frame.f_trace = None
283 stop = True
284
285 else:
286 stop = False
287
288 if django_stop:
289 frame = suspend_django(self, mainDebugger, thread, frame)
290 if frame:
291 self.doWaitSuspend(thread, frame, event, arg)
292 elif stop:
293 #event is always == line or return at this point
294 if event == 'line':
295 self.setSuspend(thread, info.pydev_step_cmd)
296 self.doWaitSuspend(thread, frame, event, arg)
297 else: #return event
298 back = frame.f_back
299 if back is not None:
300 #When we get to the pydevd run function, the debugging has actually finished for the main thread
301 #(note that it can still go on for other threads, but for this one, we just make it finish)
302 #So, just setting it to None should be OK
303 if basename(back.f_code.co_filename) == 'pydevd.py' and back.f_code.co_name == 'run':
304 back = None
305
306 if back is not None:
307 #if we're in a return, we want it to appear to the user in the previous frame!
308 self.setSuspend(thread, info.pydev_step_cmd)
309 self.doWaitSuspend(thread, back, event, arg)
310 else:
311 #in jython we may not have a back frame
312 info.pydev_step_stop = None
313 info.pydev_step_cmd = None
314 info.pydev_state = STATE_RUN
315
316
317 except:
318 traceback.print_exc()
319 info.pydev_step_cmd = None
320
321 #if we are quitting, let's stop the tracing
322 retVal = None
323 if not mainDebugger.quitting:
324 retVal = self.trace_dispatch
325
326 return retVal
327 finally:
328 info.is_tracing = False
329
330 #end trace_dispatch
331
332 if USE_PSYCO_OPTIMIZATION:
333 try:
334 import psyco
335
336 trace_dispatch = psyco.proxy(trace_dispatch)
337 except ImportError:
338 if hasattr(sys, 'exc_clear'): #jython does not have it
339 sys.exc_clear() #don't keep the traceback
340 pass #ok, psyco not available
341
342 def shouldStopOnDjangoBreak(self, frame, event, arg):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700343 mainDebugger, filename, info, thread = self._args
344 flag = False
345 filename = get_template_file_name(frame)
346 pydev_log.debug("Django is rendering a template: %s\n" % filename)
347 django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
348 if django_breakpoints_for_file:
349 pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
350 template_line = get_template_line(frame)
351 pydev_log.debug("Tracing template line: %d\n" % template_line)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800352
Tor Norbyec667c1f2014-05-28 17:06:51 -0700353 if DictContains(django_breakpoints_for_file, template_line):
354 django_breakpoint = django_breakpoints_for_file[template_line]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800355
Tor Norbyec667c1f2014-05-28 17:06:51 -0700356 if django_breakpoint.is_triggered(frame):
357 pydev_log.debug("Breakpoint is triggered.\n")
358 flag = True
359 new_frame = DjangoTemplateFrame(frame)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800360
Tor Norbyec667c1f2014-05-28 17:06:51 -0700361 if django_breakpoint.condition is not None:
362 try:
363 val = eval(django_breakpoint.condition, new_frame.f_globals, new_frame.f_locals)
364 if not val:
365 flag = False
366 pydev_log.debug("Condition '%s' is evaluated to %s. Not suspending.\n" % (django_breakpoint.condition, val))
367 except:
368 pydev_log.info(
369 'Error while evaluating condition \'%s\': %s\n' % (django_breakpoint.condition, sys.exc_info()[1]))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800370
Tor Norbyec667c1f2014-05-28 17:06:51 -0700371 if django_breakpoint.expression is not None:
372 try:
373 try:
374 val = eval(django_breakpoint.expression, new_frame.f_globals, new_frame.f_locals)
375 except:
376 val = sys.exc_info()[1]
377 finally:
378 if val is not None:
379 thread.additionalInfo.message = val
380 if flag:
381 frame = suspend_django(self, mainDebugger, thread, frame)
382 return (flag, frame)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800383
384def add_exception_to_frame(frame, exception_info):
385 frame.f_locals['__exception__'] = exception_info