| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 1 | import linecache |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 2 | import os.path |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 3 | import re |
| 4 | import traceback # @Reimport |
| 5 | |
| 6 | from django_debug import find_django_render_frame |
| 7 | from django_debug import is_django_render_call, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call |
| 8 | from django_frame import DjangoTemplateFrame |
| 9 | from django_frame import is_django_exception_break_context |
| 10 | from django_frame import just_raised, get_template_file_name, get_template_line |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 11 | import pydev_log |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 12 | from pydevd_breakpoints import get_exception_breakpoint, get_exception_name |
| 13 | from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \ |
| 14 | CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \ |
| 15 | CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT |
| 16 | from pydevd_constants import * # @UnusedWildImport |
| 17 | from pydevd_file_utils import GetFilenameAndBase |
| Tor Norbye | c3d3a90 | 2014-09-04 13:24:04 -0700 | [diff] [blame^] | 18 | try: |
| 19 | from pydevd_signature import sendSignatureCallTrace |
| 20 | except ImportError: |
| 21 | def sendSignatureCallTrace(*args, **kwargs): |
| 22 | pass |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 23 | import pydevd_vars |
| 24 | import pydevd_dont_trace |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 25 | |
| 26 | basename = os.path.basename |
| 27 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 28 | IGNORE_EXCEPTION_TAG = re.compile('[^#]*#.*@IgnoreException') |
| 29 | |
| 30 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 31 | #======================================================================================================================= |
| 32 | # PyDBFrame |
| 33 | #======================================================================================================================= |
| 34 | class PyDBFrame: |
| 35 | '''This makes the tracing for a given frame, so, the trace_dispatch |
| 36 | is used initially when we enter into a new context ('call') and then |
| 37 | is reused for the entire context. |
| 38 | ''' |
| 39 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 40 | #Note: class (and not instance) attributes. |
| 41 | |
| 42 | #Same thing in the main debugger but only considering the file contents, while the one in the main debugger |
| 43 | #considers the user input (so, the actual result must be a join of both). |
| 44 | filename_to_lines_where_exceptions_are_ignored = {} |
| 45 | filename_to_stat_info = {} |
| 46 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 47 | def __init__(self, args): |
| 48 | #args = mainDebugger, filename, base, info, t, frame |
| 49 | #yeap, much faster than putting in self and then getting it from self later on |
| 50 | self._args = args[:-1] |
| 51 | |
| 52 | def setSuspend(self, *args, **kwargs): |
| 53 | self._args[0].setSuspend(*args, **kwargs) |
| 54 | |
| 55 | def doWaitSuspend(self, *args, **kwargs): |
| 56 | self._args[0].doWaitSuspend(*args, **kwargs) |
| 57 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 58 | def _is_django_render_call(self, frame): |
| 59 | try: |
| 60 | return self._cached_is_django_render_call |
| 61 | except: |
| 62 | # Calculate lazily: note that a PyDBFrame always deals with the same |
| 63 | # frame over and over, so, we can cache this. |
| 64 | # -- although we can't cache things which change over time (such as |
| 65 | # the breakpoints for the file). |
| 66 | ret = self._cached_is_django_render_call = is_django_render_call(frame) |
| 67 | return ret |
| 68 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 69 | def trace_exception(self, frame, event, arg): |
| 70 | if event == 'exception': |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 71 | flag, frame = self.should_stop_on_exception(frame, event, arg) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 72 | |
| 73 | if flag: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 74 | self.handle_exception(frame, event, arg) |
| 75 | return self.trace_dispatch |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 76 | |
| 77 | return self.trace_exception |
| 78 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 79 | def should_stop_on_exception(self, frame, event, arg): |
| 80 | mainDebugger, _filename, info, thread = self._args |
| 81 | flag = False |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 82 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 83 | if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None: |
| 84 | exception, value, trace = arg |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 85 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 86 | if trace is not None: #on jython trace is None on the first event |
| 87 | exception_breakpoint = get_exception_breakpoint( |
| 88 | exception, mainDebugger.break_on_caught_exceptions) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 89 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 90 | if exception_breakpoint is not None: |
| 91 | if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace): |
| 92 | # print frame.f_code.co_name |
| 93 | add_exception_to_frame(frame, (exception, value, trace)) |
| 94 | thread.additionalInfo.message = exception_breakpoint.qname |
| 95 | flag = True |
| 96 | else: |
| 97 | flag = False |
| 98 | else: |
| 99 | try: |
| 100 | if mainDebugger.django_exception_break and get_exception_name(exception) in [ |
| 101 | 'VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] \ |
| 102 | and just_raised(trace) and is_django_exception_break_context(frame): |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 103 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 104 | render_frame = find_django_render_frame(frame) |
| 105 | if render_frame: |
| 106 | suspend_frame = suspend_django( |
| 107 | self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK) |
| 108 | |
| 109 | if suspend_frame: |
| 110 | add_exception_to_frame(suspend_frame, (exception, value, trace)) |
| 111 | flag = True |
| 112 | thread.additionalInfo.message = 'VariableDoesNotExist' |
| 113 | suspend_frame.f_back = frame |
| 114 | frame = suspend_frame |
| 115 | except : |
| 116 | flag = False |
| 117 | |
| 118 | return flag, frame |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 119 | |
| 120 | def handle_exception(self, frame, event, arg): |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 121 | try: |
| 122 | # print 'handle_exception', frame.f_lineno, frame.f_code.co_name |
| 123 | |
| 124 | # We have 3 things in arg: exception type, description, traceback object |
| 125 | trace_obj = arg[2] |
| 126 | mainDebugger = self._args[0] |
| 127 | |
| 128 | if not hasattr(trace_obj, 'tb_next'): |
| 129 | return #Not always there on Jython... |
| 130 | |
| 131 | initial_trace_obj = trace_obj |
| 132 | if trace_obj.tb_next is None and trace_obj.tb_frame is frame: |
| 133 | #I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check). |
| 134 | |
| 135 | if mainDebugger.break_on_exceptions_thrown_in_same_context: |
| 136 | #Option: Don't break if an exception is caught in the same function from which it is thrown |
| 137 | return |
| 138 | else: |
| 139 | #Get the trace_obj from where the exception was raised... |
| 140 | while trace_obj.tb_next is not None: |
| 141 | trace_obj = trace_obj.tb_next |
| 142 | |
| 143 | |
| 144 | if mainDebugger.ignore_exceptions_thrown_in_lines_with_ignore_exception: |
| 145 | for check_trace_obj in (initial_trace_obj, trace_obj): |
| 146 | filename = GetFilenameAndBase(check_trace_obj.tb_frame)[0] |
| 147 | |
| 148 | |
| 149 | filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored |
| 150 | |
| 151 | |
| 152 | lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename) |
| 153 | if lines_ignored is None: |
| 154 | lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {} |
| 155 | |
| 156 | try: |
| 157 | curr_stat = os.stat(filename) |
| 158 | curr_stat = (curr_stat.st_size, curr_stat.st_mtime) |
| 159 | except: |
| 160 | curr_stat = None |
| 161 | |
| 162 | last_stat = self.filename_to_stat_info.get(filename) |
| 163 | if last_stat != curr_stat: |
| 164 | self.filename_to_stat_info[filename] = curr_stat |
| 165 | lines_ignored.clear() |
| 166 | try: |
| 167 | linecache.checkcache(filename) |
| 168 | except: |
| 169 | #Jython 2.1 |
| 170 | linecache.checkcache() |
| 171 | |
| 172 | from_user_input = mainDebugger.filename_to_lines_where_exceptions_are_ignored.get(filename) |
| 173 | if from_user_input: |
| 174 | merged = {} |
| 175 | merged.update(lines_ignored) |
| 176 | #Override what we have with the related entries that the user entered |
| 177 | merged.update(from_user_input) |
| 178 | else: |
| 179 | merged = lines_ignored |
| 180 | |
| 181 | exc_lineno = check_trace_obj.tb_lineno |
| 182 | |
| 183 | # print ('lines ignored', lines_ignored) |
| 184 | # print ('user input', from_user_input) |
| 185 | # print ('merged', merged, 'curr', exc_lineno) |
| 186 | |
| 187 | if not DictContains(merged, exc_lineno): #Note: check on merged but update lines_ignored. |
| 188 | try: |
| 189 | line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals) |
| 190 | except: |
| 191 | #Jython 2.1 |
| 192 | line = linecache.getline(filename, exc_lineno) |
| 193 | |
| 194 | if IGNORE_EXCEPTION_TAG.match(line) is not None: |
| 195 | lines_ignored[exc_lineno] = 1 |
| 196 | return |
| 197 | else: |
| 198 | #Put in the cache saying not to ignore |
| 199 | lines_ignored[exc_lineno] = 0 |
| 200 | else: |
| 201 | #Ok, dict has it already cached, so, let's check it... |
| 202 | if merged.get(exc_lineno, 0): |
| 203 | return |
| 204 | |
| 205 | |
| 206 | thread = self._args[3] |
| 207 | |
| 208 | try: |
| 209 | frame_id_to_frame = {} |
| 210 | frame_id_to_frame[id(frame)] = frame |
| 211 | f = trace_obj.tb_frame |
| 212 | while f is not None: |
| 213 | frame_id_to_frame[id(f)] = f |
| 214 | f = f.f_back |
| 215 | f = None |
| 216 | |
| 217 | thread_id = GetThreadId(thread) |
| 218 | pydevd_vars.addAdditionalFrameById(thread_id, frame_id_to_frame) |
| 219 | try: |
| 220 | mainDebugger.sendCaughtExceptionStack(thread, arg, id(frame)) |
| 221 | self.setSuspend(thread, CMD_STEP_CAUGHT_EXCEPTION) |
| 222 | self.doWaitSuspend(thread, frame, event, arg) |
| 223 | mainDebugger.sendCaughtExceptionStackProceeded(thread) |
| 224 | |
| 225 | finally: |
| 226 | pydevd_vars.removeAdditionalFrameById(thread_id) |
| 227 | except: |
| 228 | traceback.print_exc() |
| 229 | |
| 230 | mainDebugger.SetTraceForFrameAndParents(frame) |
| 231 | finally: |
| 232 | #Clear some local variables... |
| 233 | trace_obj = None |
| 234 | initial_trace_obj = None |
| 235 | check_trace_obj = None |
| 236 | f = None |
| 237 | frame_id_to_frame = None |
| 238 | mainDebugger = None |
| 239 | thread = None |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 240 | |
| 241 | def trace_dispatch(self, frame, event, arg): |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 242 | main_debugger, filename, info, thread = self._args |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 243 | try: |
| 244 | info.is_tracing = True |
| 245 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 246 | if main_debugger._finishDebuggingSession: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 247 | return None |
| 248 | |
| 249 | if getattr(thread, 'pydev_do_not_trace', None): |
| 250 | return None |
| 251 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 252 | if event == 'call' and main_debugger.signature_factory: |
| 253 | sendSignatureCallTrace(main_debugger, frame, filename) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 254 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 255 | is_exception_event = event == 'exception' |
| 256 | has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.django_exception_break |
| 257 | |
| 258 | if is_exception_event: |
| 259 | if has_exception_breakpoints: |
| 260 | flag, frame = self.should_stop_on_exception(frame, event, arg) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 261 | if flag: |
| 262 | self.handle_exception(frame, event, arg) |
| 263 | return self.trace_dispatch |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 264 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 265 | elif event not in ('line', 'call', 'return'): |
| 266 | #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace. |
| 267 | return None |
| 268 | |
| 269 | stop_frame = info.pydev_step_stop |
| 270 | step_cmd = info.pydev_step_cmd |
| 271 | |
| 272 | if is_exception_event: |
| 273 | breakpoints_for_file = None |
| 274 | else: |
| 275 | # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break |
| 276 | # eventually. Force the step mode to step into and the step stop frame to None. |
| 277 | # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user |
| 278 | # to make a step in or step over at that location). |
| 279 | # Note: this is especially troublesome when we're skipping code with the |
| 280 | # @DontTrace comment. |
| 281 | if stop_frame is frame and event in ('return', 'exception') and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER): |
| 282 | info.pydev_step_cmd = CMD_STEP_INTO |
| 283 | info.pydev_step_stop = None |
| 284 | |
| 285 | breakpoints_for_file = main_debugger.breakpoints.get(filename) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 286 | |
| 287 | can_skip = False |
| 288 | |
| 289 | if info.pydev_state == STATE_RUN: |
| 290 | #we can skip if: |
| 291 | #- we have no stop marked |
| 292 | #- we should make a step return/step over and we're not in the current frame |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 293 | can_skip = (step_cmd is None and stop_frame is None)\ |
| 294 | or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 295 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 296 | check_stop_on_django_render_call = main_debugger.django_breakpoints and self._is_django_render_call(frame) |
| 297 | if check_stop_on_django_render_call: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 298 | can_skip = False |
| 299 | |
| 300 | # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, |
| 301 | # we will return nothing for the next trace |
| 302 | #also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, |
| 303 | #so, that's why the additional checks are there. |
| 304 | if not breakpoints_for_file: |
| 305 | if can_skip: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 306 | if has_exception_breakpoints: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 307 | return self.trace_exception |
| 308 | else: |
| 309 | return None |
| 310 | |
| 311 | else: |
| 312 | #checks the breakpoint to see if there is a context match in some function |
| 313 | curr_func_name = frame.f_code.co_name |
| 314 | |
| 315 | #global context is set with an empty name |
| 316 | if curr_func_name in ('?', '<module>'): |
| 317 | curr_func_name = '' |
| 318 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 319 | for breakpoint in DictIterValues(breakpoints_for_file): #jython does not support itervalues() |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 320 | #will match either global or some function |
| 321 | if breakpoint.func_name in ('None', curr_func_name): |
| 322 | break |
| 323 | |
| 324 | else: # if we had some break, it won't get here (so, that's a context that we want to skip) |
| 325 | if can_skip: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 326 | if has_exception_breakpoints: |
| 327 | return self.trace_exception |
| 328 | else: |
| 329 | return None |
| 330 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 331 | |
| 332 | #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 |
| 333 | #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event |
| 334 | |
| 335 | try: |
| 336 | line = frame.f_lineno |
| 337 | |
| 338 | |
| 339 | flag = False |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 340 | if event == 'call' and info.pydev_state != STATE_SUSPEND and check_stop_on_django_render_call: |
| 341 | flag, frame = self.should_stop_on_django_breakpoint(frame, event, arg) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 342 | |
| 343 | #return is not taken into account for breakpoint hit because we'd have a double-hit in this case |
| 344 | #(one for the line and the other for the return). |
| 345 | |
| 346 | if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\ |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 347 | and DictContains(breakpoints_for_file, line): |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 348 | #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint |
| 349 | # lets do the conditional stuff here |
| 350 | breakpoint = breakpoints_for_file[line] |
| 351 | |
| 352 | stop = True |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 353 | if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'): |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 354 | stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later) |
| 355 | else: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 356 | condition = breakpoint.condition |
| 357 | if condition is not None: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 358 | try: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 359 | val = eval(condition, frame.f_globals, frame.f_locals) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 360 | if not val: |
| 361 | return self.trace_dispatch |
| 362 | |
| 363 | except: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 364 | if type(condition) != type(''): |
| 365 | if hasattr(condition, 'encode'): |
| 366 | condition = condition.encode('utf-8') |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 367 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 368 | msg = 'Error while evaluating expression: %s\n' % (condition,) |
| 369 | sys.stderr.write(msg) |
| 370 | traceback.print_exc() |
| 371 | if not main_debugger.suspend_on_breakpoint_exception: |
| 372 | return self.trace_dispatch |
| 373 | else: |
| 374 | stop = True |
| 375 | try: |
| 376 | additional_info = None |
| 377 | try: |
| 378 | additional_info = thread.additionalInfo |
| 379 | except AttributeError: |
| 380 | pass #that's ok, no info currently set |
| 381 | |
| 382 | if additional_info is not None: |
| 383 | # add exception_type and stacktrace into thread additional info |
| 384 | etype, value, tb = sys.exc_info() |
| 385 | try: |
| 386 | error = ''.join(traceback.format_exception_only(etype, value)) |
| 387 | stack = traceback.extract_stack(f=tb.tb_frame.f_back) |
| 388 | |
| 389 | # On self.setSuspend(thread, CMD_SET_BREAK) this info will be |
| 390 | # sent to the client. |
| 391 | additional_info.conditional_breakpoint_exception = \ |
| 392 | ('Condition:\n' + condition + '\n\nError:\n' + error, stack) |
| 393 | finally: |
| 394 | etype, value, tb = None, None, None |
| 395 | except: |
| 396 | traceback.print_exc() |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 397 | |
| 398 | if breakpoint.expression is not None: |
| 399 | try: |
| 400 | try: |
| 401 | val = eval(breakpoint.expression, frame.f_globals, frame.f_locals) |
| 402 | except: |
| 403 | val = sys.exc_info()[1] |
| 404 | finally: |
| 405 | if val is not None: |
| 406 | thread.additionalInfo.message = val |
| 407 | |
| 408 | if stop: |
| 409 | self.setSuspend(thread, CMD_SET_BREAK) |
| 410 | |
| 411 | # if thread has a suspend flag, we suspend with a busy wait |
| 412 | if info.pydev_state == STATE_SUSPEND: |
| 413 | self.doWaitSuspend(thread, frame, event, arg) |
| 414 | return self.trace_dispatch |
| 415 | |
| 416 | except: |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 417 | traceback.print_exc() |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 418 | raise |
| 419 | |
| 420 | #step handling. We stop when we hit the right frame |
| 421 | try: |
| 422 | django_stop = False |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 423 | |
| 424 | should_skip = False |
| 425 | if pydevd_dont_trace.should_trace_hook is not None: |
| 426 | if not hasattr(self, 'should_skip'): |
| 427 | # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). |
| 428 | # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code |
| 429 | # Which will be handled by this frame is read-only, so, we can cache it safely. |
| 430 | should_skip = self.should_skip = not pydevd_dont_trace.should_trace_hook(frame, filename) |
| 431 | else: |
| 432 | should_skip = self.should_skip |
| 433 | |
| 434 | if should_skip: |
| 435 | stop = False |
| 436 | |
| 437 | elif step_cmd == CMD_STEP_INTO: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 438 | stop = event in ('line', 'return') |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 439 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 440 | if is_django_suspended(thread): |
| 441 | #django_stop = event == 'call' and is_django_render_call(frame) |
| 442 | stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame) |
| 443 | if stop: |
| 444 | info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame |
| 445 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 446 | elif step_cmd == CMD_STEP_OVER: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 447 | if is_django_suspended(thread): |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 448 | django_stop = event == 'call' and self._is_django_render_call(frame) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 449 | |
| 450 | stop = False |
| 451 | else: |
| 452 | if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back): |
| 453 | #we return to Django suspend mode and should not stop before django rendering frame |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 454 | stop_frame = info.pydev_step_stop = info.pydev_django_resolve_frame |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 455 | info.pydev_django_resolve_frame = None |
| 456 | thread.additionalInfo.suspend_type = DJANGO_SUSPEND |
| 457 | |
| 458 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 459 | stop = stop_frame is frame and event in ('line', 'return') |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 460 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 461 | elif step_cmd == CMD_SMART_STEP_INTO: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 462 | stop = False |
| 463 | if info.pydev_smart_step_stop is frame: |
| 464 | info.pydev_func_name = None |
| 465 | info.pydev_smart_step_stop = None |
| 466 | |
| 467 | if event == 'line' or event == 'exception': |
| 468 | curr_func_name = frame.f_code.co_name |
| 469 | |
| 470 | #global context is set with an empty name |
| 471 | if curr_func_name in ('?', '<module>') or curr_func_name is None: |
| 472 | curr_func_name = '' |
| 473 | |
| 474 | if curr_func_name == info.pydev_func_name: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 475 | stop = True |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 476 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 477 | elif step_cmd == CMD_STEP_RETURN: |
| 478 | stop = event == 'return' and stop_frame is frame |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 479 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 480 | elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 481 | stop = False |
| 482 | |
| 483 | if event == 'line' or event == 'exception': |
| 484 | #Yes, we can only act on line events (weird hum?) |
| 485 | #Note: This code is duplicated at pydevd.py |
| 486 | #Acting on exception events after debugger breaks with exception |
| 487 | curr_func_name = frame.f_code.co_name |
| 488 | |
| 489 | #global context is set with an empty name |
| 490 | if curr_func_name in ('?', '<module>'): |
| 491 | curr_func_name = '' |
| 492 | |
| 493 | if curr_func_name == info.pydev_func_name: |
| 494 | line = info.pydev_next_line |
| 495 | if frame.f_lineno == line: |
| 496 | stop = True |
| 497 | else: |
| 498 | if frame.f_trace is None: |
| 499 | frame.f_trace = self.trace_dispatch |
| 500 | frame.f_lineno = line |
| 501 | frame.f_trace = None |
| 502 | stop = True |
| 503 | |
| 504 | else: |
| 505 | stop = False |
| 506 | |
| 507 | if django_stop: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 508 | frame = suspend_django(self, main_debugger, thread, frame) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 509 | if frame: |
| 510 | self.doWaitSuspend(thread, frame, event, arg) |
| 511 | elif stop: |
| 512 | #event is always == line or return at this point |
| 513 | if event == 'line': |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 514 | self.setSuspend(thread, step_cmd) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 515 | self.doWaitSuspend(thread, frame, event, arg) |
| 516 | else: #return event |
| 517 | back = frame.f_back |
| 518 | if back is not None: |
| 519 | #When we get to the pydevd run function, the debugging has actually finished for the main thread |
| 520 | #(note that it can still go on for other threads, but for this one, we just make it finish) |
| 521 | #So, just setting it to None should be OK |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 522 | base = basename(back.f_code.co_filename) |
| 523 | if base == 'pydevd.py' and back.f_code.co_name == 'run': |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 524 | back = None |
| 525 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 526 | elif base == 'pydevd_traceproperty.py': |
| 527 | # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) |
| 528 | #if we're in a return, we want it to appear to the user in the previous frame! |
| 529 | return None |
| 530 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 531 | if back is not None: |
| 532 | #if we're in a return, we want it to appear to the user in the previous frame! |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 533 | self.setSuspend(thread, step_cmd) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 534 | self.doWaitSuspend(thread, back, event, arg) |
| 535 | else: |
| 536 | #in jython we may not have a back frame |
| 537 | info.pydev_step_stop = None |
| 538 | info.pydev_step_cmd = None |
| 539 | info.pydev_state = STATE_RUN |
| 540 | |
| 541 | |
| 542 | except: |
| 543 | traceback.print_exc() |
| 544 | info.pydev_step_cmd = None |
| 545 | |
| 546 | #if we are quitting, let's stop the tracing |
| 547 | retVal = None |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 548 | if not main_debugger.quitting: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 549 | retVal = self.trace_dispatch |
| 550 | |
| 551 | return retVal |
| 552 | finally: |
| 553 | info.is_tracing = False |
| 554 | |
| 555 | #end trace_dispatch |
| 556 | |
| 557 | if USE_PSYCO_OPTIMIZATION: |
| 558 | try: |
| 559 | import psyco |
| 560 | |
| 561 | trace_dispatch = psyco.proxy(trace_dispatch) |
| 562 | except ImportError: |
| 563 | if hasattr(sys, 'exc_clear'): #jython does not have it |
| 564 | sys.exc_clear() #don't keep the traceback |
| 565 | pass #ok, psyco not available |
| 566 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 567 | def should_stop_on_django_breakpoint(self, frame, event, arg): |
| 568 | mainDebugger = self._args[0] |
| 569 | thread = self._args[3] |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 570 | flag = False |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 571 | template_frame_file = get_template_file_name(frame) |
| 572 | |
| 573 | #pydev_log.debug("Django is rendering a template: %s\n" % template_frame_file) |
| 574 | |
| 575 | django_breakpoints_for_file = mainDebugger.django_breakpoints.get(template_frame_file) |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 576 | if django_breakpoints_for_file: |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 577 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 578 | #pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 579 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 580 | template_frame_line = get_template_line(frame, template_frame_file) |
| 581 | |
| 582 | #pydev_log.debug("Tracing template line: %d\n" % template_frame_line) |
| 583 | |
| 584 | if DictContains(django_breakpoints_for_file, template_frame_line): |
| 585 | django_breakpoint = django_breakpoints_for_file[template_frame_line] |
| 586 | |
| 587 | if django_breakpoint.is_triggered(template_frame_file, template_frame_line): |
| 588 | |
| 589 | #pydev_log.debug("Breakpoint is triggered.\n") |
| 590 | |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 591 | flag = True |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 592 | new_frame = DjangoTemplateFrame( |
| 593 | frame, |
| 594 | template_frame_file=template_frame_file, |
| 595 | template_frame_line=template_frame_line, |
| 596 | ) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 597 | |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 598 | if django_breakpoint.condition is not None: |
| 599 | try: |
| 600 | val = eval(django_breakpoint.condition, new_frame.f_globals, new_frame.f_locals) |
| 601 | if not val: |
| 602 | flag = False |
| 603 | pydev_log.debug("Condition '%s' is evaluated to %s. Not suspending.\n" % (django_breakpoint.condition, val)) |
| 604 | except: |
| 605 | pydev_log.info( |
| 606 | 'Error while evaluating condition \'%s\': %s\n' % (django_breakpoint.condition, sys.exc_info()[1])) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 607 | |
| Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 608 | if django_breakpoint.expression is not None: |
| 609 | try: |
| 610 | try: |
| 611 | val = eval(django_breakpoint.expression, new_frame.f_globals, new_frame.f_locals) |
| 612 | except: |
| 613 | val = sys.exc_info()[1] |
| 614 | finally: |
| 615 | if val is not None: |
| 616 | thread.additionalInfo.message = val |
| 617 | if flag: |
| 618 | frame = suspend_django(self, mainDebugger, thread, frame) |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame] | 619 | return flag, frame |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 620 | |
| 621 | def add_exception_to_frame(frame, exception_info): |
| 622 | frame.f_locals['__exception__'] = exception_info |