blob: a91f16060211473af2e64e71515ae135661a83e7 [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:
213 raise
214
215 #step handling. We stop when we hit the right frame
216 try:
217 django_stop = False
218 if info.pydev_step_cmd == CMD_STEP_INTO:
219 stop = event in ('line', 'return')
220 if is_django_suspended(thread):
221 #django_stop = event == 'call' and is_django_render_call(frame)
222 stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame)
223 if stop:
224 info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame
225
226 elif info.pydev_step_cmd == CMD_STEP_OVER:
227 if is_django_suspended(thread):
228 django_stop = event == 'call' and is_django_render_call(frame)
229
230 stop = False
231 else:
232 if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back):
233 #we return to Django suspend mode and should not stop before django rendering frame
234 info.pydev_step_stop = info.pydev_django_resolve_frame
235 info.pydev_django_resolve_frame = None
236 thread.additionalInfo.suspend_type = DJANGO_SUSPEND
237
238
239 stop = info.pydev_step_stop is frame and event in ('line', 'return')
240
241 elif info.pydev_step_cmd == CMD_SMART_STEP_INTO:
242 stop = False
243 if info.pydev_smart_step_stop is frame:
244 info.pydev_func_name = None
245 info.pydev_smart_step_stop = None
246
247 if event == 'line' or event == 'exception':
248 curr_func_name = frame.f_code.co_name
249
250 #global context is set with an empty name
251 if curr_func_name in ('?', '<module>') or curr_func_name is None:
252 curr_func_name = ''
253
254 if curr_func_name == info.pydev_func_name:
255 stop = True
256
257 elif info.pydev_step_cmd == CMD_STEP_RETURN:
258 stop = event == 'return' and info.pydev_step_stop is frame
259
260 elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT:
261 stop = False
262
263 if event == 'line' or event == 'exception':
264 #Yes, we can only act on line events (weird hum?)
265 #Note: This code is duplicated at pydevd.py
266 #Acting on exception events after debugger breaks with exception
267 curr_func_name = frame.f_code.co_name
268
269 #global context is set with an empty name
270 if curr_func_name in ('?', '<module>'):
271 curr_func_name = ''
272
273 if curr_func_name == info.pydev_func_name:
274 line = info.pydev_next_line
275 if frame.f_lineno == line:
276 stop = True
277 else:
278 if frame.f_trace is None:
279 frame.f_trace = self.trace_dispatch
280 frame.f_lineno = line
281 frame.f_trace = None
282 stop = True
283
284 else:
285 stop = False
286
287 if django_stop:
288 frame = suspend_django(self, mainDebugger, thread, frame)
289 if frame:
290 self.doWaitSuspend(thread, frame, event, arg)
291 elif stop:
292 #event is always == line or return at this point
293 if event == 'line':
294 self.setSuspend(thread, info.pydev_step_cmd)
295 self.doWaitSuspend(thread, frame, event, arg)
296 else: #return event
297 back = frame.f_back
298 if back is not None:
299 #When we get to the pydevd run function, the debugging has actually finished for the main thread
300 #(note that it can still go on for other threads, but for this one, we just make it finish)
301 #So, just setting it to None should be OK
302 if basename(back.f_code.co_filename) == 'pydevd.py' and back.f_code.co_name == 'run':
303 back = None
304
305 if back is not None:
306 #if we're in a return, we want it to appear to the user in the previous frame!
307 self.setSuspend(thread, info.pydev_step_cmd)
308 self.doWaitSuspend(thread, back, event, arg)
309 else:
310 #in jython we may not have a back frame
311 info.pydev_step_stop = None
312 info.pydev_step_cmd = None
313 info.pydev_state = STATE_RUN
314
315
316 except:
317 traceback.print_exc()
318 info.pydev_step_cmd = None
319
320 #if we are quitting, let's stop the tracing
321 retVal = None
322 if not mainDebugger.quitting:
323 retVal = self.trace_dispatch
324
325 return retVal
326 finally:
327 info.is_tracing = False
328
329 #end trace_dispatch
330
331 if USE_PSYCO_OPTIMIZATION:
332 try:
333 import psyco
334
335 trace_dispatch = psyco.proxy(trace_dispatch)
336 except ImportError:
337 if hasattr(sys, 'exc_clear'): #jython does not have it
338 sys.exc_clear() #don't keep the traceback
339 pass #ok, psyco not available
340
341 def shouldStopOnDjangoBreak(self, frame, event, arg):
342 mainDebugger, filename, info, thread = self._args
343 flag = False
344 filename = get_template_file_name(frame)
345 pydev_log.debug("Django is rendering a template: %s\n" % filename)
346 django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
347 if django_breakpoints_for_file:
348 pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
349 template_line = get_template_line(frame)
350 pydev_log.debug("Tracing template line: %d\n" % template_line)
351
352 if DictContains(django_breakpoints_for_file, template_line):
353 django_breakpoint = django_breakpoints_for_file[template_line]
354
355 if django_breakpoint.is_triggered(frame):
356 pydev_log.debug("Breakpoint is triggered.\n")
357 flag = True
358 new_frame = DjangoTemplateFrame(frame)
359
360 if django_breakpoint.condition is not None:
361 try:
362 val = eval(django_breakpoint.condition, new_frame.f_globals, new_frame.f_locals)
363 if not val:
364 flag = False
365 pydev_log.debug("Condition '%s' is evaluated to %s. Not suspending.\n" %(django_breakpoint.condition, val))
366 except:
367 pydev_log.info('Error while evaluating condition \'%s\': %s\n' % (django_breakpoint.condition, sys.exc_info()[1]))
368
369 if django_breakpoint.expression is not None:
370 try:
371 try:
372 val = eval(django_breakpoint.expression, new_frame.f_globals, new_frame.f_locals)
373 except:
374 val = sys.exc_info()[1]
375 finally:
376 if val is not None:
377 thread.additionalInfo.message = val
378 if flag:
379 frame = suspend_django(self, mainDebugger, thread, frame)
380 return (flag, frame)
381
382def add_exception_to_frame(frame, exception_info):
383 frame.f_locals['__exception__'] = exception_info