Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git

Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py
index 05faaeb..374d281 100644
--- a/python/helpers/pydev/pydevd_frame.py
+++ b/python/helpers/pydev/pydevd_frame.py
@@ -1,18 +1,29 @@
-from 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
-from django_debug import find_django_render_frame
-from django_frame import just_raised
-from django_frame import is_django_exception_break_context
-from django_frame import DjangoTemplateFrame
-from pydevd_comm import * #@UnusedWildImport
-from pydevd_breakpoints import * #@UnusedWildImport
-import traceback #@Reimport
+import linecache
 import os.path
-import sys
+import re
+import traceback  # @Reimport
+
+from django_debug import find_django_render_frame
+from django_debug import is_django_render_call, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
+from django_frame import DjangoTemplateFrame
+from django_frame import is_django_exception_break_context
+from django_frame import just_raised, get_template_file_name, get_template_line
 import pydev_log
+from pydevd_breakpoints import get_exception_breakpoint, get_exception_name
+from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \
+    CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \
+    CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT
+from pydevd_constants import *  # @UnusedWildImport
+from pydevd_file_utils import GetFilenameAndBase
 from pydevd_signature import sendSignatureCallTrace
+import pydevd_vars
+import pydevd_dont_trace
 
 basename = os.path.basename
 
+IGNORE_EXCEPTION_TAG = re.compile('[^#]*#.*@IgnoreException')
+
+
 #=======================================================================================================================
 # PyDBFrame
 #=======================================================================================================================
@@ -22,6 +33,13 @@
     is reused for the entire context.
     '''
 
+    #Note: class (and not instance) attributes.
+
+    #Same thing in the main debugger but only considering the file contents, while the one in the main debugger
+    #considers the user input (so, the actual result must be a join of both).
+    filename_to_lines_where_exceptions_are_ignored = {}
+    filename_to_stat_info = {}
+
     def __init__(self, args):
         #args = mainDebugger, filename, base, info, t, frame
         #yeap, much faster than putting in self and then getting it from self later on
@@ -33,84 +51,234 @@
     def doWaitSuspend(self, *args, **kwargs):
         self._args[0].doWaitSuspend(*args, **kwargs)
 
+    def _is_django_render_call(self, frame):
+        try:
+            return self._cached_is_django_render_call
+        except:
+            # Calculate lazily: note that a PyDBFrame always deals with the same
+            # frame over and over, so, we can cache this.
+            # -- although we can't cache things which change over time (such as
+            #    the breakpoints for the file).
+            ret = self._cached_is_django_render_call = is_django_render_call(frame)
+            return ret
+
     def trace_exception(self, frame, event, arg):
         if event == 'exception':
-            (flag, frame) = self.shouldStopOnException(frame, event, arg)
+            flag, frame = self.should_stop_on_exception(frame, event, arg)
 
             if flag:
-              self.handle_exception(frame, event, arg)
-              return self.trace_dispatch
+                self.handle_exception(frame, event, arg)
+                return self.trace_dispatch
 
         return self.trace_exception
 
-    def shouldStopOnException(self, frame, event, arg):
-      mainDebugger, filename, info, thread = self._args
-      flag = False
+    def should_stop_on_exception(self, frame, event, arg):
+        mainDebugger, _filename, info, thread = self._args
+        flag = False
 
-      if info.pydev_state != STATE_SUSPEND:  #and breakpoint is not None:
-          (exception, value, trace) = arg
+        if info.pydev_state != STATE_SUSPEND:  #and breakpoint is not None:
+            exception, value, trace = arg
 
-          if trace is not None: #on jython trace is None on the first event
-              exception_breakpoint = get_exception_breakpoint(exception, dict(mainDebugger.exception_set), NOTIFY_ALWAYS)
-              if exception_breakpoint is not None:
-                  if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
-                      curr_func_name = frame.f_code.co_name
-                      add_exception_to_frame(frame, (exception, value, trace))
-                      self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK)
-                      thread.additionalInfo.message = exception_breakpoint.qname
-                      flag = True
-                  else:
-                      flag = False
-              else:
-                  try:
-                      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):
-                          render_frame = find_django_render_frame(frame)
-                          if render_frame:
-                              suspend_frame = suspend_django(self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
+            if trace is not None: #on jython trace is None on the first event
+                exception_breakpoint = get_exception_breakpoint(
+                    exception, mainDebugger.break_on_caught_exceptions)
 
-                              if suspend_frame:
-                                  add_exception_to_frame(suspend_frame, (exception, value, trace))
-                                  flag = True
-                                  thread.additionalInfo.message = 'VariableDoesNotExist'
-                                  suspend_frame.f_back = frame
-                                  frame = suspend_frame
-                  except :
-                      flag = False
+                if exception_breakpoint is not None:
+                    if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
+                        # print frame.f_code.co_name
+                        add_exception_to_frame(frame, (exception, value, trace))
+                        thread.additionalInfo.message = exception_breakpoint.qname
+                        flag = True
+                    else:
+                        flag = False
+                else:
+                    try:
+                        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):
 
-      return (flag, frame)
+                            render_frame = find_django_render_frame(frame)
+                            if render_frame:
+                                suspend_frame = suspend_django(
+                                    self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
+
+                                if suspend_frame:
+                                    add_exception_to_frame(suspend_frame, (exception, value, trace))
+                                    flag = True
+                                    thread.additionalInfo.message = 'VariableDoesNotExist'
+                                    suspend_frame.f_back = frame
+                                    frame = suspend_frame
+                    except :
+                        flag = False
+
+        return flag, frame
 
     def handle_exception(self, frame, event, arg):
-      mainDebugger = self._args[0]
-      thread = self._args[3]
-      self.doWaitSuspend(thread, frame, event, arg)
-      mainDebugger.SetTraceForFrameAndParents(frame)
+        try:
+            # print 'handle_exception', frame.f_lineno, frame.f_code.co_name
+
+            # We have 3 things in arg: exception type, description, traceback object
+            trace_obj = arg[2]
+            mainDebugger = self._args[0]
+
+            if not hasattr(trace_obj, 'tb_next'):
+                return  #Not always there on Jython...
+
+            initial_trace_obj = trace_obj
+            if trace_obj.tb_next is None and trace_obj.tb_frame is frame:
+                #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).
+
+                if mainDebugger.break_on_exceptions_thrown_in_same_context:
+                    #Option: Don't break if an exception is caught in the same function from which it is thrown
+                    return
+            else:
+                #Get the trace_obj from where the exception was raised...
+                while trace_obj.tb_next is not None:
+                    trace_obj = trace_obj.tb_next
+
+
+            if mainDebugger.ignore_exceptions_thrown_in_lines_with_ignore_exception:
+                for check_trace_obj in (initial_trace_obj, trace_obj):
+                    filename = GetFilenameAndBase(check_trace_obj.tb_frame)[0]
+
+
+                    filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored
+
+
+                    lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename)
+                    if lines_ignored is None:
+                        lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {}
+
+                    try:
+                        curr_stat = os.stat(filename)
+                        curr_stat = (curr_stat.st_size, curr_stat.st_mtime)
+                    except:
+                        curr_stat = None
+
+                    last_stat = self.filename_to_stat_info.get(filename)
+                    if last_stat != curr_stat:
+                        self.filename_to_stat_info[filename] = curr_stat
+                        lines_ignored.clear()
+                        try:
+                            linecache.checkcache(filename)
+                        except:
+                            #Jython 2.1
+                            linecache.checkcache()
+
+                    from_user_input = mainDebugger.filename_to_lines_where_exceptions_are_ignored.get(filename)
+                    if from_user_input:
+                        merged = {}
+                        merged.update(lines_ignored)
+                        #Override what we have with the related entries that the user entered
+                        merged.update(from_user_input)
+                    else:
+                        merged = lines_ignored
+
+                    exc_lineno = check_trace_obj.tb_lineno
+
+                    # print ('lines ignored', lines_ignored)
+                    # print ('user input', from_user_input)
+                    # print ('merged', merged, 'curr', exc_lineno)
+
+                    if not DictContains(merged, exc_lineno):  #Note: check on merged but update lines_ignored.
+                        try:
+                            line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals)
+                        except:
+                            #Jython 2.1
+                            line = linecache.getline(filename, exc_lineno)
+
+                        if IGNORE_EXCEPTION_TAG.match(line) is not None:
+                            lines_ignored[exc_lineno] = 1
+                            return
+                        else:
+                            #Put in the cache saying not to ignore
+                            lines_ignored[exc_lineno] = 0
+                    else:
+                        #Ok, dict has it already cached, so, let's check it...
+                        if merged.get(exc_lineno, 0):
+                            return
+
+
+            thread = self._args[3]
+
+            try:
+                frame_id_to_frame = {}
+                frame_id_to_frame[id(frame)] = frame
+                f = trace_obj.tb_frame
+                while f is not None:
+                    frame_id_to_frame[id(f)] = f
+                    f = f.f_back
+                f = None
+
+                thread_id = GetThreadId(thread)
+                pydevd_vars.addAdditionalFrameById(thread_id, frame_id_to_frame)
+                try:
+                    mainDebugger.sendCaughtExceptionStack(thread, arg, id(frame))
+                    self.setSuspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
+                    self.doWaitSuspend(thread, frame, event, arg)
+                    mainDebugger.sendCaughtExceptionStackProceeded(thread)
+
+                finally:
+                    pydevd_vars.removeAdditionalFrameById(thread_id)
+            except:
+                traceback.print_exc()
+
+            mainDebugger.SetTraceForFrameAndParents(frame)
+        finally:
+            #Clear some local variables...
+            trace_obj = None
+            initial_trace_obj = None
+            check_trace_obj = None
+            f = None
+            frame_id_to_frame = None
+            mainDebugger = None
+            thread = None
 
     def trace_dispatch(self, frame, event, arg):
-        mainDebugger, filename, info, thread = self._args
+        main_debugger, filename, info, thread = self._args
         try:
             info.is_tracing = True
 
-            if mainDebugger._finishDebuggingSession:
+            if main_debugger._finishDebuggingSession:
                 return None
 
             if getattr(thread, 'pydev_do_not_trace', None):
                 return None
 
-            if event == 'call':
-                sendSignatureCallTrace(mainDebugger, frame, filename)
+            if event == 'call' and main_debugger.signature_factory:
+                sendSignatureCallTrace(main_debugger, frame, filename)
 
-            if event not in ('line', 'call', 'return'):
-                if event == 'exception':
-                    (flag, frame) = self.shouldStopOnException(frame, event, arg)
+            is_exception_event = event == 'exception'
+            has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.django_exception_break
+
+            if is_exception_event:
+                if has_exception_breakpoints:
+                    flag, frame = self.should_stop_on_exception(frame, event, arg)
                     if flag:
                         self.handle_exception(frame, event, arg)
                         return self.trace_dispatch
-                else:
-                #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
-                    return None
 
-            if event is not 'exception':
-                breakpoints_for_file = mainDebugger.breakpoints.get(filename)
+            elif event not in ('line', 'call', 'return'):
+                #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
+                return None
+
+            stop_frame = info.pydev_step_stop
+            step_cmd = info.pydev_step_cmd
+
+            if is_exception_event:
+                breakpoints_for_file = None
+            else:
+                # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break
+                # eventually.  Force the step mode to step into and the step stop frame to None.
+                # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user
+                # to make a step in or step over at that location).
+                # Note: this is especially troublesome when we're skipping code with the
+                # @DontTrace comment.
+                if stop_frame is frame and event in ('return', 'exception') and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
+                    info.pydev_step_cmd = CMD_STEP_INTO
+                    info.pydev_step_stop = None
+
+                breakpoints_for_file = main_debugger.breakpoints.get(filename)
 
                 can_skip = False
 
@@ -118,10 +286,11 @@
                     #we can skip if:
                     #- we have no stop marked
                     #- we should make a step return/step over and we're not in the current frame
-                    can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\
-                    or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame)
+                    can_skip = (step_cmd is None and stop_frame is None)\
+                        or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)
 
-                if  mainDebugger.django_breakpoints:
+                check_stop_on_django_render_call = main_debugger.django_breakpoints and self._is_django_render_call(frame)
+                if check_stop_on_django_render_call:
                     can_skip = False
 
                 # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
@@ -130,7 +299,7 @@
                 #so, that's why the additional checks are there.
                 if not breakpoints_for_file:
                     if can_skip:
-                        if mainDebugger.always_exception_set or mainDebugger.django_exception_break:
+                        if has_exception_breakpoints:
                             return self.trace_exception
                         else:
                             return None
@@ -143,17 +312,18 @@
                     if curr_func_name in ('?', '<module>'):
                         curr_func_name = ''
 
-                    for breakpoint in breakpoints_for_file.values(): #jython does not support itervalues()
+                    for breakpoint in DictIterValues(breakpoints_for_file): #jython does not support itervalues()
                         #will match either global or some function
                         if breakpoint.func_name in ('None', curr_func_name):
                             break
 
                     else: # if we had some break, it won't get here (so, that's a context that we want to skip)
                         if can_skip:
-                            #print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd
-                            return None
-            else:
-                breakpoints_for_file = None
+                            if has_exception_breakpoints:
+                                return self.trace_exception
+                            else:
+                                return None
+
 
             #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
             #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
@@ -163,33 +333,63 @@
 
 
                 flag = False
-                if event == 'call' and info.pydev_state != STATE_SUSPEND and mainDebugger.django_breakpoints \
-                and is_django_render_call(frame):
-                    (flag, frame) = self.shouldStopOnDjangoBreak(frame, event, arg)
+                if event == 'call' and info.pydev_state != STATE_SUSPEND and check_stop_on_django_render_call:
+                    flag, frame = self.should_stop_on_django_breakpoint(frame, event, arg)
 
                 #return is not taken into account for breakpoint hit because we'd have a double-hit in this case
                 #(one for the line and the other for the return).
 
                 if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\
-                and DictContains(breakpoints_for_file, line):
+                    and DictContains(breakpoints_for_file, line):
                     #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
                     # lets do the conditional stuff here
                     breakpoint = breakpoints_for_file[line]
 
                     stop = True
-                    if info.pydev_step_cmd == CMD_STEP_OVER and info.pydev_step_stop is frame and event in ('line', 'return'):
+                    if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'):
                         stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
                     else:
-                        if breakpoint.condition is not None:
+                        condition = breakpoint.condition
+                        if condition is not None:
                             try:
-                                val = eval(breakpoint.condition, frame.f_globals, frame.f_locals)
+                                val = eval(condition, frame.f_globals, frame.f_locals)
                                 if not val:
                                     return self.trace_dispatch
 
                             except:
-                                pydev_log.info('Error while evaluating condition \'%s\': %s\n' % (breakpoint.condition, sys.exc_info()[1]))
+                                if type(condition) != type(''):
+                                    if hasattr(condition, 'encode'):
+                                        condition = condition.encode('utf-8')
 
-                                return self.trace_dispatch
+                                msg = 'Error while evaluating expression: %s\n' % (condition,)
+                                sys.stderr.write(msg)
+                                traceback.print_exc()
+                                if not main_debugger.suspend_on_breakpoint_exception:
+                                    return self.trace_dispatch
+                                else:
+                                    stop = True
+                                    try:
+                                        additional_info = None
+                                        try:
+                                            additional_info = thread.additionalInfo
+                                        except AttributeError:
+                                            pass  #that's ok, no info currently set
+
+                                        if additional_info is not None:
+                                            # add exception_type and stacktrace into thread additional info
+                                            etype, value, tb = sys.exc_info()
+                                            try:
+                                                error = ''.join(traceback.format_exception_only(etype, value))
+                                                stack = traceback.extract_stack(f=tb.tb_frame.f_back)
+
+                                                # On self.setSuspend(thread, CMD_SET_BREAK) this info will be
+                                                # sent to the client.
+                                                additional_info.conditional_breakpoint_exception = \
+                                                    ('Condition:\n' + condition + '\n\nError:\n' + error, stack)
+                                            finally:
+                                                etype, value, tb = None, None, None
+                                    except:
+                                        traceback.print_exc()
 
                     if breakpoint.expression is not None:
                         try:
@@ -216,30 +416,45 @@
             #step handling. We stop when we hit the right frame
             try:
                 django_stop = False
-                if info.pydev_step_cmd == CMD_STEP_INTO:
+
+                should_skip = False
+                if pydevd_dont_trace.should_trace_hook is not None:
+                    if not hasattr(self, 'should_skip'):
+                        # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times).
+                        # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code
+                        # Which will be handled by this frame is read-only, so, we can cache it safely.
+                        should_skip = self.should_skip = not pydevd_dont_trace.should_trace_hook(frame, filename)
+                    else:
+                        should_skip = self.should_skip
+
+                if should_skip:
+                    stop = False
+
+                elif step_cmd == CMD_STEP_INTO:
                     stop = event in ('line', 'return')
+
                     if is_django_suspended(thread):
                         #django_stop = event == 'call' and is_django_render_call(frame)
                         stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame)
                         if stop:
                             info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame
 
-                elif info.pydev_step_cmd == CMD_STEP_OVER:
+                elif step_cmd == CMD_STEP_OVER:
                     if is_django_suspended(thread):
-                        django_stop = event == 'call' and is_django_render_call(frame)
+                        django_stop = event == 'call' and self._is_django_render_call(frame)
 
                         stop = False
                     else:
                         if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back):
                             #we return to Django suspend mode and should not stop before django rendering frame
-                            info.pydev_step_stop = info.pydev_django_resolve_frame
+                            stop_frame = info.pydev_step_stop = info.pydev_django_resolve_frame
                             info.pydev_django_resolve_frame = None
                             thread.additionalInfo.suspend_type = DJANGO_SUSPEND
 
 
-                        stop = info.pydev_step_stop is frame and event in ('line', 'return')
+                        stop = stop_frame is frame and event in ('line', 'return')
 
-                elif info.pydev_step_cmd == CMD_SMART_STEP_INTO:
+                elif step_cmd == CMD_SMART_STEP_INTO:
                     stop = False
                     if info.pydev_smart_step_stop is frame:
                         info.pydev_func_name = None
@@ -253,12 +468,12 @@
                             curr_func_name = ''
 
                         if curr_func_name == info.pydev_func_name:
-                                stop = True
+                            stop = True
 
-                elif info.pydev_step_cmd == CMD_STEP_RETURN:
-                    stop = event == 'return' and info.pydev_step_stop is frame
+                elif step_cmd == CMD_STEP_RETURN:
+                    stop = event == 'return' and stop_frame is frame
 
-                elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT:
+                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                     stop = False
 
                     if event == 'line' or event == 'exception':
@@ -286,13 +501,13 @@
                     stop = False
 
                 if django_stop:
-                    frame = suspend_django(self, mainDebugger, thread, frame)
+                    frame = suspend_django(self, main_debugger, thread, frame)
                     if frame:
                         self.doWaitSuspend(thread, frame, event, arg)
                 elif stop:
                     #event is always == line or return at this point
                     if event == 'line':
-                        self.setSuspend(thread, info.pydev_step_cmd)
+                        self.setSuspend(thread, step_cmd)
                         self.doWaitSuspend(thread, frame, event, arg)
                     else: #return event
                         back = frame.f_back
@@ -300,12 +515,18 @@
                             #When we get to the pydevd run function, the debugging has actually finished for the main thread
                             #(note that it can still go on for other threads, but for this one, we just make it finish)
                             #So, just setting it to None should be OK
-                            if basename(back.f_code.co_filename) == 'pydevd.py' and back.f_code.co_name == 'run':
+                            base = basename(back.f_code.co_filename)
+                            if base == 'pydevd.py' and back.f_code.co_name == 'run':
                                 back = None
 
+                            elif base == 'pydevd_traceproperty.py':
+                                # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging)
+                                #if we're in a return, we want it to appear to the user in the previous frame!
+                                return None
+
                         if back is not None:
                             #if we're in a return, we want it to appear to the user in the previous frame!
-                            self.setSuspend(thread, info.pydev_step_cmd)
+                            self.setSuspend(thread, step_cmd)
                             self.doWaitSuspend(thread, back, event, arg)
                         else:
                             #in jython we may not have a back frame
@@ -320,7 +541,7 @@
 
             #if we are quitting, let's stop the tracing
             retVal = None
-            if not mainDebugger.quitting:
+            if not main_debugger.quitting:
                 retVal = self.trace_dispatch
 
             return retVal
@@ -339,24 +560,36 @@
                 sys.exc_clear() #don't keep the traceback
             pass #ok, psyco not available
 
-    def shouldStopOnDjangoBreak(self, frame, event, arg):
-        mainDebugger, filename, info, thread = self._args
+    def should_stop_on_django_breakpoint(self, frame, event, arg):
+        mainDebugger = self._args[0]
+        thread = self._args[3]
         flag = False
-        filename = get_template_file_name(frame)
-        pydev_log.debug("Django is rendering a template: %s\n" % filename)
-        django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
+        template_frame_file = get_template_file_name(frame)
+
+        #pydev_log.debug("Django is rendering a template: %s\n" % template_frame_file)
+
+        django_breakpoints_for_file = mainDebugger.django_breakpoints.get(template_frame_file)
         if django_breakpoints_for_file:
-            pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
-            template_line = get_template_line(frame)
-            pydev_log.debug("Tracing template line: %d\n" % template_line)
 
-            if DictContains(django_breakpoints_for_file, template_line):
-                django_breakpoint = django_breakpoints_for_file[template_line]
+            #pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
 
-                if django_breakpoint.is_triggered(frame):
-                    pydev_log.debug("Breakpoint is triggered.\n")
+            template_frame_line = get_template_line(frame, template_frame_file)
+
+            #pydev_log.debug("Tracing template line: %d\n" % template_frame_line)
+
+            if DictContains(django_breakpoints_for_file, template_frame_line):
+                django_breakpoint = django_breakpoints_for_file[template_frame_line]
+
+                if django_breakpoint.is_triggered(template_frame_file, template_frame_line):
+
+                    #pydev_log.debug("Breakpoint is triggered.\n")
+
                     flag = True
-                    new_frame = DjangoTemplateFrame(frame)
+                    new_frame = DjangoTemplateFrame(
+                        frame,
+                        template_frame_file=template_frame_file,
+                        template_frame_line=template_frame_line,
+                    )
 
                     if django_breakpoint.condition is not None:
                         try:
@@ -379,7 +612,7 @@
                                 thread.additionalInfo.message = val
                     if flag:
                         frame = suspend_django(self, mainDebugger, thread, frame)
-        return (flag, frame)
+        return flag, frame
 
 def add_exception_to_frame(frame, exception_info):
     frame.f_locals['__exception__'] = exception_info
\ No newline at end of file