diff --git a/python/helpers/pydev/django_debug.py b/python/helpers/pydev/django_debug.py
new file mode 100644
index 000000000000..754c01a9781c
--- /dev/null
+++ b/python/helpers/pydev/django_debug.py
@@ -0,0 +1,151 @@
+import inspect
+from pydev.pydevd_comm import CMD_SET_BREAK
+from pydev.pydevd_constants import DJANGO_SUSPEND, GetThreadId
+from pydev.pydevd_file_utils import NormFileToServer
+from pydev.runfiles import DictContains
+from pydevd_breakpoints import LineBreakpoint
+import traceback
+
+
+def get_source(frame):
+ try:
+ return frame.f_locals['self'].source
+ except:
+ return None
+
+def get_template_file_name(frame):
+ try:
+ source = get_source(frame)
+ return source[0].name
+ except:
+ return None
+
+def get_template_line(frame):
+ source = get_source(frame)
+ file_name = get_template_file_name(frame)
+ try:
+ return offset_to_line_number(read_file(file_name), source[1][0])
+ except:
+ return None
+
+class DjangoTemplateFrame:
+ def __init__(self, frame):
+ file_name = get_template_file_name(frame)
+ context = frame.f_locals['context']
+ self.f_code = FCode('Django Template', file_name)
+ self.f_lineno = get_template_line(frame)
+ self.f_back = frame
+ self.f_globals = {}
+ self.f_locals = collect_context(context)
+ self.f_trace = None
+
+
+class FCode:
+ def __init__(self, name, filename):
+ self.co_name = name
+ self.co_filename = filename
+
+
+def collect_context(context):
+ res = {}
+ for d in context.dicts:
+ for k,v in d.items():
+ res[k] = v
+ return res
+
+def read_file(filename):
+ f = open(filename, "r")
+ s = f.read()
+ f.close()
+ return s
+
+def offset_to_line_number(text, offset):
+ curLine = 1
+ curOffset = 0
+ while curOffset < offset:
+ if curOffset == len(text):
+ return -1
+ c = text[curOffset]
+ if c == '\n':
+ curLine += 1
+ elif c == '\r':
+ curLine += 1
+ if curOffset < len(text) and text[curOffset + 1] == '\n':
+ curOffset += 1
+
+ curOffset += 1
+
+ return curLine
+
+class DjangoLineBreakpoint(LineBreakpoint):
+ def __init__(self, type, file, line, flag, condition, func_name, expression):
+ self.file = file
+ self.line = line
+ LineBreakpoint.__init__(self, type, flag, condition, func_name, expression)
+
+ def __eq__(self, other):
+ if not isinstance(other, DjangoLineBreakpoint):
+ return False
+ return self.file == other.file and self.line == other.line
+
+ def is_triggered(self, frame):
+ file = get_template_file_name(frame)
+ line = get_template_line(frame)
+ return self.file == file and self.line == line
+
+def is_django_render_call(frame):
+ try:
+ name = frame.f_code.co_name
+ if name != 'render':
+ return False
+
+ if not DictContains(frame.f_locals, 'self'):
+ return False
+
+ cls = frame.f_locals['self'].__class__
+
+ inherits_node = False
+ for base in inspect.getmro(cls):
+ if base.__name__ == 'Node':
+ inherits_node = True
+ break
+
+ if not inherits_node:
+ return False
+
+ clsname = cls.__name__
+ return clsname != 'TextNode' and clsname != 'NodeList'
+ except :
+ traceback.print_exc()
+ return False
+
+def is_django_suspended(thread):
+ return thread.additionalInfo.suspend_type == DJANGO_SUSPEND
+
+def suspend_django(py_db_frame, mainDebugger, thread, frame):
+ frame = DjangoTemplateFrame(frame)
+
+ if frame.f_lineno is None:
+ return None
+
+ #try:
+ # if thread.additionalInfo.filename == frame.f_code.co_filename and thread.additionalInfo.line == frame.f_lineno:
+ # return None # don't stay twice on the same line
+ #except AttributeError:
+ # pass
+
+ mainDebugger.additional_frames.addAdditionalFrameById(GetThreadId(thread), {id(frame): frame})
+
+
+ py_db_frame.setSuspend(thread, CMD_SET_BREAK)
+ thread.additionalInfo.suspend_type = DJANGO_SUSPEND
+
+ thread.additionalInfo.filename = frame.f_code.co_filename
+ thread.additionalInfo.line = frame.f_lineno
+
+ return frame
+
+
+
+
+
diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py
index ca21c92baeed..2255455ba938 100644
--- a/python/helpers/pydev/pydevd.py
+++ b/python/helpers/pydev/pydevd.py
@@ -1,4 +1,5 @@
#IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe
+from pydev.django_debug import DjangoLineBreakpoint
from pydevd_constants import * #@UnusedWildImport
from pydevd_breakpoints import * #@UnusedWildImport
@@ -48,6 +49,7 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \
from pydevd_file_utils import NormFileToServer, GetFilenameAndBase
import pydevd_vars
+from pydevd_vars import getAdditionalFramesContainer
import traceback
import pydevd_vm_type
import pydevd_tracing
@@ -194,6 +196,8 @@ class PyDB:
self.cmdFactory = NetCommandFactory()
self.cmdQueue = {} # the hash of Queues. Key is thread id, value is thread
self.breakpoints = {}
+ self.django_breakpoints = {}
+ self.additional_frames = getAdditionalFramesContainer()
self.readyToRun = False
self.lock = threading.RLock()
self.internalQueueLock = threading.Lock()
@@ -529,7 +533,8 @@ class PyDB:
#command to add some breakpoint.
# text is file\tline. Add to breakpoints dictionary
- file, line, condition, expression = text.split('\t', 3)
+ type, file, line, condition, expression = text.split('\t', 4)
+
if condition.startswith('**FUNC**'):
func_name, condition = condition.split('\t', 1)
@@ -552,32 +557,29 @@ class PyDB:
line = int(line)
- if DEBUG_TRACE_BREAKPOINTS > 0:
- sys.stderr.write('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
- sys.stderr.flush()
-
- if DictContains(self.breakpoints, file):
- breakDict = self.breakpoints[file]
- else:
- breakDict = {}
-
if len(condition) <= 0 or condition is None or condition == "None":
condition = None
if len(expression) <= 0 or expression is None or expression == "None":
expression = None
- breakDict[line] = (True, condition, func_name, expression)
+ if type == 'python-line':
+ breakpoint = LineBreakpoint(type, True, condition, func_name, expression)
+ breakpoint.add(self.breakpoints, file, line, func_name)
+ elif type == 'django-line':
+ breakpoint = DjangoLineBreakpoint(type, file, line, True, condition, func_name, expression)
+ breakpoint.add(self.django_breakpoints, file, line, func_name)
+ else:
+ raise NameError(type)
- self.breakpoints[file] = breakDict
self.enable_tracing()
elif cmd_id == CMD_REMOVE_BREAK:
#command to remove some breakpoint
#text is file\tline. Remove from breakpoints dictionary
- file, line = text.split('\t', 1)
+ type, file, line = text.split('\t', 2)
file = NormFileToServer(file)
try:
line = int(line)
@@ -586,7 +588,10 @@ class PyDB:
else:
try:
- del self.breakpoints[file][line] #remove the breakpoint in that line
+ if type == 'django-line':
+ del self.django_breakpoints[file][line]
+ else:
+ del self.breakpoints[file][line] #remove the breakpoint in that line
if DEBUG_TRACE_BREAKPOINTS > 0:
sys.stderr.write('Removed breakpoint:%s\n' % (file,))
sys.stderr.flush()
@@ -685,6 +690,7 @@ class PyDB:
thread.additionalInfo.pydev_notify_kill = True
def setSuspend(self, thread, stop_reason):
+ thread.additionalInfo.suspend_type = PYTHON_SUSPEND
thread.additionalInfo.pydev_state = STATE_SUSPEND
thread.stop_reason = stop_reason
@@ -816,13 +822,13 @@ class PyDB:
self.force_post_mortem_stop -= 1
frame, frames_byid = additionalInfo.pydev_force_stop_at_exception
thread_id = GetThreadId(t)
- used_id = pydevd_vars.addAdditionalFrameById(thread_id, frames_byid)
+ used_id = pydev_vars.additional_frames_container.addAdditionalFrameById(thread_id, frames_byid)
try:
self.setSuspend(t, CMD_ADD_EXCEPTION_BREAK)
self.doWaitSuspend(t, frame, 'exception', None)
finally:
additionalInfo.pydev_force_stop_at_exception = None
- pydevd_vars.removeAdditionalFrameById(thread_id)
+ pydev_vars.additional_frames_container.removeAdditionalFrameById(thread_id)
# if thread is not alive, cancel trace_dispatch processing
if not t.isAlive():
diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py
index a9317b479422..ebac70801a8d 100644
--- a/python/helpers/pydev/pydevd_breakpoints.py
+++ b/python/helpers/pydev/pydevd_breakpoints.py
@@ -21,6 +21,34 @@ class ExceptionBreakpoint:
self.type = exctype
self.notify = {NOTIFY_ALWAYS: notify_always, NOTIFY_ON_TERMINATE: notify_on_terminate}
+class LineBreakpoint:
+ def __init__(self, type, flag, condition, func_name, expression):
+ self.type = type
+ self.condition = condition
+ self.func_name = func_name
+ self.expression = expression
+
+ def get_break_dict(self, breakpoints, file):
+ if DictContains(breakpoints, file):
+ breakDict = breakpoints[file]
+ else:
+ breakDict = {}
+ breakpoints[file] = breakDict
+ return breakDict
+
+ def trace(self, file, line, func_name):
+ if DEBUG_TRACE_BREAKPOINTS > 0:
+ sys.stderr.write('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
+ sys.stderr.flush()
+
+ def add(self, breakpoints, file, line, func_name):
+ self.trace(file, line, func_name)
+
+ breakDict = self.get_break_dict(breakpoints, file)
+
+ breakDict[line] = self
+
+
def get_exception_breakpoint(exctype, exceptions, notify_class):
exc = None
if exceptions is not None:
diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py
index e0a51a0a2f7e..788d4cf7f92b 100644
--- a/python/helpers/pydev/pydevd_comm.py
+++ b/python/helpers/pydev/pydevd_comm.py
@@ -481,38 +481,41 @@ class NetCommandFactory:
try:
cmdTextList = [""]
cmdTextList.append('' % (thread_id, stop_reason, message))
-
- curFrame = frame
- while curFrame:
- #print cmdText
- myId = str(id(curFrame))
- #print "id is ", myId
-
- if curFrame.f_code is None:
- break #Iron Python sometimes does not have it!
-
- myName = curFrame.f_code.co_name #method name (if in method) or ? if global
- if myName is None:
- break #Iron Python sometimes does not have it!
-
- #print "name is ", myName
-
- myFile = pydevd_file_utils.NormFileToClient(curFrame.f_code.co_filename)
- #print "file is ", myFile
- #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
-
- myLine = str(curFrame.f_lineno)
- #print "line is ", myLine
-
- #the variables are all gotten 'on-demand'
- #variables = pydevd_vars.frameVarsToXML(curFrame)
- variables = ''
- cmdTextList.append('"' % (quote(myFile, '/>_= \t'), myLine))
- cmdTextList.append(variables)
- cmdTextList.append("")
- curFrame = curFrame.f_back
+ curFrame = frame
+ try:
+ while curFrame:
+ #print cmdText
+ myId = str(id(curFrame))
+ #print "id is ", myId
+
+ if curFrame.f_code is None:
+ break #Iron Python sometimes does not have it!
+
+ myName = curFrame.f_code.co_name #method name (if in method) or ? if global
+ if myName is None:
+ break #Iron Python sometimes does not have it!
+
+ #print "name is ", myName
+
+ myFile = pydevd_file_utils.NormFileToClient(curFrame.f_code.co_filename)
+ #print "file is ", myFile
+ #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
+
+ myLine = str(curFrame.f_lineno)
+ #print "line is ", myLine
+
+ #the variables are all gotten 'on-demand'
+ #variables = pydevd_vars.frameVarsToXML(curFrame)
+
+ variables = ''
+ cmdTextList.append('"' % (quote(myFile, '/>_= \t'), myLine))
+ cmdTextList.append(variables)
+ cmdTextList.append("")
+ curFrame = curFrame.f_back
+ except :
+ traceback.print_exc()
cmdTextList.append("")
cmdText = ''.join(cmdTextList)
@@ -778,7 +781,7 @@ class InternalGetCompletions(InternalThreadCommand):
try:
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
-
+
#Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
#(Names not resolved in generator expression in method)
#See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
diff --git a/python/helpers/pydev/pydevd_constants.py b/python/helpers/pydev/pydevd_constants.py
index b8b1aa4c2c6d..4f0f88a2f44b 100644
--- a/python/helpers/pydev/pydevd_constants.py
+++ b/python/helpers/pydev/pydevd_constants.py
@@ -5,6 +5,9 @@ This module holds the constants used for specifying the states of the debugger.
STATE_RUN = 1
STATE_SUSPEND = 2
+PYTHON_SUSPEND = 1
+DJANGO_SUSPEND = 2
+
try:
__setFalse = False
except:
diff --git a/python/helpers/pydev/pydevd_file_utils.py b/python/helpers/pydev/pydevd_file_utils.py
index ab5357ce38a9..f29169ea51c8 100644
--- a/python/helpers/pydev/pydevd_file_utils.py
+++ b/python/helpers/pydev/pydevd_file_utils.py
@@ -191,11 +191,11 @@ if PATHS_FROM_ECLIPSE_TO_PYTHON:
except KeyError:
#used to translate a path from the debug server to the client
translated = _NormFile(filename)
- for eclipse_prefix, pyhon_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
- if translated.startswith(pyhon_prefix):
+ for eclipse_prefix, python_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
+ if translated.startswith(python_prefix):
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: replacing to client: %s\n' % (translated,))
- translated = translated.replace(pyhon_prefix, eclipse_prefix)
+ translated = translated.replace(python_prefix, eclipse_prefix)
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: sent to client: %s\n' % (translated,))
break
diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py
index 44edd63c7a8a..14d748350c3d 100644
--- a/python/helpers/pydev/pydevd_frame.py
+++ b/python/helpers/pydev/pydevd_frame.py
@@ -1,3 +1,5 @@
+from pydev.django_debug import DjangoTemplateFrame, DjangoLineBreakpoint, is_django_render_call, get_template_file_name, get_template_line, is_django_suspended, suspend_django
+import pydevd_vars
from pydevd_comm import * #@UnusedWildImport
from pydevd_constants import * #@UnusedWildImport
from pydevd_breakpoints import * #@UnusedWildImport
@@ -15,18 +17,18 @@ class PyDBFrame:
is used initially when we enter into a new context ('call') and then
is reused for the entire context.
'''
-
+
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
self._args = args[:-1]
-
+
def setSuspend(self, *args, **kwargs):
self._args[0].setSuspend(*args, **kwargs)
-
+
def doWaitSuspend(self, *args, **kwargs):
self._args[0].doWaitSuspend(*args, **kwargs)
-
+
def trace_dispatch(self, frame, event, arg):
if event not in ('line', 'call', 'return', 'exception'):
return None
@@ -34,10 +36,11 @@ class PyDBFrame:
mainDebugger, filename, info, thread = self._args
if event is not 'exception':
- breakpoint = mainDebugger.breakpoints.get(filename)
+ breakpoints_for_file = mainDebugger.breakpoints.get(filename)
can_skip = False
- if len(always_exception_set) == 0:
+
+ if len(always_exception_set):
if info.pydev_state == STATE_RUN:
#we can skip if:
#- we have no stop marked
@@ -45,14 +48,16 @@ class PyDBFrame:
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)
+ if mainDebugger.django_breakpoints:
+ 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,
# we will return nothing for the next trace
#also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
#so, that's why the additional checks are there.
- if not breakpoint:
+ if not breakpoints_for_file:
if can_skip:
- return None
+ return None
else:
#checks the breakpoint to see if there is a context match in some function
@@ -62,9 +67,9 @@ class PyDBFrame:
if curr_func_name in ('?', ''):
curr_func_name = ''
- for _b, condition, func_name, expression in breakpoint.values(): #jython does not support itervalues()
+ for breakpoint in breakpoints_for_file.values(): #jython does not support itervalues()
#will match either global or some function
- if func_name in ('None', curr_func_name):
+ 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)
@@ -72,7 +77,7 @@ class PyDBFrame:
#print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd
return None
else:
- breakpoint = None
+ breakpoints_for_file = 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
@@ -91,31 +96,47 @@ class PyDBFrame:
thread.additionalInfo.message = exception_breakpoint.qname
#self.doWaitSuspend(thread, frame, event, arg)
+ elif event == 'call' and info.pydev_state != STATE_SUSPEND and mainDebugger.django_breakpoints \
+ and is_django_render_call(frame):
+ flag = False
+ filename = get_template_file_name(frame)
+ django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
+ if django_breakpoints_for_file:
+ template_line = get_template_line(frame)
+ if DictContains(django_breakpoints_for_file, template_line):
+ django_breakpoint = django_breakpoints_for_file[template_line]
+
+ if django_breakpoint.is_triggered(frame):
+ frame = suspend_django(self, mainDebugger, thread, frame)
+ flag = True
+
+ #if not flag:
+ # return self.trace_dispatch
+
+
#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).
- elif event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoint is not None \
- and DictContains(breakpoint, line):
-
+ elif event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\
+ 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
- condition = breakpoint[line][1]
+ breakpoint = breakpoints_for_file[line]
- if condition is not None:
+ if breakpoint.condition is not None:
try:
- val = eval(condition, frame.f_globals, frame.f_locals)
+ val = eval(breakpoint.condition, frame.f_globals, frame.f_locals)
if not val:
return self.trace_dispatch
-
+
except:
- sys.stderr.write('Error while evaluating condition \'%s\': %s\n'%(condition, sys.exc_info()[1]))
+ sys.stderr.write('Error while evaluating condition \'%s\': %s\n' % (breakpoint.condition, sys.exc_info()[1]))
sys.stderr.flush()
return self.trace_dispatch
- expression = breakpoint[line][3]
- if expression is not None:
+ if breakpoint.expression is not None:
try:
try:
- val = eval(expression, frame.f_globals, frame.f_locals)
+ val = eval(breakpoint.expression, frame.f_globals, frame.f_locals)
except:
val = sys.exc_info()[1]
finally:
@@ -123,41 +144,45 @@ class PyDBFrame:
thread.log_expression = val
self.setSuspend(thread, CMD_SET_BREAK)
-
+
# if thread has a suspend flag, we suspend with a busy wait
if info.pydev_state == STATE_SUSPEND:
self.doWaitSuspend(thread, frame, event, arg)
return self.trace_dispatch
-
+
except:
raise
-
+
#step handling. We stop when we hit the right frame
try:
-
+ django_stop = False
if info.pydev_step_cmd == CMD_STEP_INTO:
-
stop = event in ('line', 'return')
-
+
elif info.pydev_step_cmd == CMD_STEP_OVER:
-
- stop = info.pydev_step_stop is frame and event in ('line', 'return')
-
+ if is_django_suspended(thread):
+
+ django_stop = event == 'call' and is_django_render_call(frame)
+
+ stop = False
+ else:
+ stop = info.pydev_step_stop is frame and event in ('line', 'return')
+
elif info.pydev_step_cmd == CMD_STEP_RETURN:
-
stop = event == 'return' and info.pydev_step_stop is frame
-
+
elif info.pydev_step_cmd == CMD_RUN_TO_LINE:
stop = False
+
if event == 'line':
#Yes, we can only act on line events (weird hum?)
#Note: This code is duplicated at pydevd.py
curr_func_name = frame.f_code.co_name
-
+
#global context is set with an empty name
if curr_func_name in ('?', ''):
curr_func_name = ''
-
+
if curr_func_name == info.pydev_func_name:
line = info.pydev_next_line
if frame.f_lineno == line:
@@ -168,11 +193,15 @@ class PyDBFrame:
frame.f_lineno = line
frame.f_trace = None
stop = True
-
+
else:
stop = False
-
- if stop:
+
+ if django_stop:
+ frame = suspend_django(self, mainDebugger, 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)
@@ -180,14 +209,12 @@ class PyDBFrame:
else: #return event
back = frame.f_back
if back is not None:
-
#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':
back = 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)
@@ -197,22 +224,23 @@ class PyDBFrame:
info.pydev_step_stop = None
info.pydev_step_cmd = None
info.pydev_state = STATE_RUN
-
-
+
+
except:
traceback.print_exc()
info.pydev_step_cmd = None
-
+
#if we are quitting, let's stop the tracing
retVal = None
if not mainDebugger.quitting:
retVal = self.trace_dispatch
return retVal
-
+
if USE_PSYCO_OPTIMIZATION:
try:
import psyco
+
trace_dispatch = psyco.proxy(trace_dispatch)
except ImportError:
if hasattr(sys, 'exc_clear'): #jython does not have it
diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py
index b73a7154d834..c2c1fa93a4d8 100644
--- a/python/helpers/pydev/pydevd_vars.py
+++ b/python/helpers/pydev/pydevd_vars.py
@@ -1,6 +1,7 @@
""" pydevd_vars deals with variables:
resolution/conversion to XML.
"""
+import pickle
from pydevd_constants import * #@UnusedWildImport
from types import * #@UnusedWildImport
from console import pydevconsole
@@ -13,6 +14,7 @@ try:
except ImportError:
from io import StringIO
import sys #@Reimport
+
try:
from urllib import quote
except:
@@ -32,30 +34,32 @@ try:
__setFalse = False
except:
import __builtin__
+
setattr(__builtin__, 'True', 1)
setattr(__builtin__, 'False', 0)
#------------------------------------------------------------------------------------------------------ class for errors
-class VariableError(RuntimeError):pass
-class FrameNotFoundError(RuntimeError):pass
+class VariableError(RuntimeError): pass
+
+class FrameNotFoundError(RuntimeError): pass
#------------------------------------------------------------------------------------------------------ resolvers in map
if not sys.platform.startswith("java"):
typeMap = [
- #None means that it should not be treated as a compound variable
+ #None means that it should not be treated as a compound variable
- #isintance does not accept a tuple on some versions of python, so, we must declare it expanded
- (type(None), None,),
- (int, None),
- (float, None),
- (complex, None),
- (str, None),
- (tuple, pydevd_resolver.tupleResolver),
- (list, pydevd_resolver.tupleResolver),
- (dict, pydevd_resolver.dictResolver),
+ #isintance does not accept a tuple on some versions of python, so, we must declare it expanded
+ (type(None), None,),
+ (int, None),
+ (float, None),
+ (complex, None),
+ (str, None),
+ (tuple, pydevd_resolver.tupleResolver),
+ (list, pydevd_resolver.tupleResolver),
+ (dict, pydevd_resolver.dictResolver),
]
try:
@@ -80,17 +84,18 @@ if not sys.platform.startswith("java"):
else: #platform is java
from org.python import core #@UnresolvedImport
+
typeMap = [
- (core.PyNone, None),
- (core.PyInteger, None),
- (core.PyLong, None),
- (core.PyFloat, None),
- (core.PyComplex, None),
- (core.PyString, None),
- (core.PyTuple, pydevd_resolver.tupleResolver),
- (core.PyList, pydevd_resolver.tupleResolver),
- (core.PyDictionary, pydevd_resolver.dictResolver),
- (core.PyStringMap, pydevd_resolver.dictResolver),
+ (core.PyNone, None),
+ (core.PyInteger, None),
+ (core.PyLong, None),
+ (core.PyFloat, None),
+ (core.PyComplex, None),
+ (core.PyString, None),
+ (core.PyTuple, pydevd_resolver.tupleResolver),
+ (core.PyList, pydevd_resolver.tupleResolver),
+ (core.PyDictionary, pydevd_resolver.dictResolver),
+ (core.PyStringMap, pydevd_resolver.dictResolver),
]
if hasattr(core, 'PyJavaInstance'):
@@ -115,7 +120,6 @@ def getType(o):
return 'Unable to get Type', 'Unable to get Type', None
try:
-
if type_name == 'org.python.core.PyJavaInstance':
return (type_object, type_name, pydevd_resolver.instanceResolver)
@@ -134,8 +138,9 @@ def getType(o):
try:
from xml.sax.saxutils import escape
+
def makeValidXmlValue(s):
- return escape(s, {'"':'"'})
+ return escape(s, {'"': '"'})
except:
#Simple replacement if it's not there.
def makeValidXmlValue(s):
@@ -177,7 +182,7 @@ def varToXML(val, name, doTrim=True):
except:
value = 'Unable to get repr for %s' % v.__class__
- xml = '0:
+ if self.skip > 0:
self.skip = self.skip - 1
else:
if data == "Traceback (most recent call last):\n":
@@ -478,7 +508,6 @@ def consoleExec(thread_id, frame_id, expression):
return False
-
def changeAttrExpression(thread_id, frame_id, attr, expression):
'''Changes some attribute in a given frame.
@note: it will not (currently) work if we're not in the topmost frame (that's a python
@@ -489,16 +518,16 @@ def changeAttrExpression(thread_id, frame_id, attr, expression):
try:
expression = expression.replace('@LINE@', '\n')
-#tests (needs proposed patch in python accepted)
-# if hasattr(frame, 'savelocals'):
-# if attr in frame.f_locals:
-# frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals)
-# frame.savelocals()
-# return
-#
-# elif attr in frame.f_globals:
-# frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
-# return
+ #tests (needs proposed patch in python accepted)
+ # if hasattr(frame, 'savelocals'):
+ # if attr in frame.f_locals:
+ # frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals)
+ # frame.savelocals()
+ # return
+ #
+ # elif attr in frame.f_globals:
+ # frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
+ # return
if attr[:7] == "Globals":
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java
index 173fe04eccdf..7e31323cf873 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java
@@ -6,20 +6,23 @@ import org.jetbrains.annotations.NotNull;
* @author traff
*/
public abstract class LineBreakpointCommand extends AbstractCommand {
+ private final String myType;
@NotNull protected final String myFile;
protected final int myLine;
+
public LineBreakpointCommand(RemoteDebugger debugger,
- int commandCode,
+ String type, int commandCode,
@NotNull final String file,
final int line) {
super(debugger, commandCode);
+ myType = type;
myFile = file;
myLine = line;
}
@Override
protected void buildPayload(Payload payload) {
- payload.add(myFile).add(Integer.toString(myLine));
+ payload.add(myType).add(myFile).add(Integer.toString(myLine));
}
}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java
index 13f664f6b9da..893989de38c3 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java
@@ -1,9 +1,11 @@
package com.jetbrains.python.debugger.pydev;
+import org.jetbrains.annotations.NotNull;
+
public class RemoveBreakpointCommand extends LineBreakpointCommand {
- public RemoveBreakpointCommand(final RemoteDebugger debugger, final String file, final int line) {
- super(debugger, REMOVE_BREAKPOINT, file, line);
+ public RemoveBreakpointCommand(final RemoteDebugger debugger, @NotNull final String type, final String file, final int line) {
+ super(debugger, type, REMOVE_BREAKPOINT, file, line);
}
}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java
index db9c538eb053..993add195579 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java
@@ -9,17 +9,19 @@ public class SetBreakpointCommand extends LineBreakpointCommand {
private @Nullable final String myLogExpression;
public SetBreakpointCommand(@NotNull final RemoteDebugger debugger,
+ @NotNull final String type,
@NotNull final String file,
@NotNull final int line) {
- this(debugger, file, line, null, null);
+ this(debugger, type, file, line, null, null);
}
public SetBreakpointCommand(@NotNull final RemoteDebugger debugger,
+ @NotNull final String type,
@NotNull final String file,
@NotNull final int line,
@Nullable final String condition,
@Nullable final String logExpression) {
- super(debugger, SET_BREAKPOINT, file, line);
+ super(debugger, type, SET_BREAKPOINT, file, line);
myCondition = condition;
myLogExpression = logExpression;
}
diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml
index 57abb96ad506..80632ecc2b1b 100644
--- a/python/src/META-INF/python-plugin-common.xml
+++ b/python/src/META-INF/python-plugin-common.xml
@@ -77,6 +77,7 @@
+
diff --git a/python/src/com/jetbrains/python/debugger/AbstractLineBreakpointHandler.java b/python/src/com/jetbrains/python/debugger/AbstractLineBreakpointHandler.java
new file mode 100644
index 000000000000..122aaeee461b
--- /dev/null
+++ b/python/src/com/jetbrains/python/debugger/AbstractLineBreakpointHandler.java
@@ -0,0 +1,52 @@
+package com.jetbrains.python.debugger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.intellij.xdebugger.XSourcePosition;
+import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
+import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
+import com.intellij.xdebugger.breakpoints.XBreakpointType;
+import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author traff
+ */
+public class AbstractLineBreakpointHandler extends XBreakpointHandler> {
+ protected final PyDebugProcess myDebugProcess;
+ private final Map, XSourcePosition> myBreakPointPositions = Maps.newHashMap();
+
+ public AbstractLineBreakpointHandler(
+ Class extends XBreakpointType, ?>> breakpointTypeClass,
+ @NotNull final PyDebugProcess debugProcess) {
+ super(breakpointTypeClass);
+ myDebugProcess = debugProcess;
+ }
+
+ public void reregisterBreakpoints() {
+ List> breakpoints = Lists.newArrayList(myBreakPointPositions.keySet());
+ for (XLineBreakpoint breakpoint : breakpoints) {
+ unregisterBreakpoint(breakpoint, false);
+ registerBreakpoint(breakpoint);
+ }
+ }
+
+ public void registerBreakpoint(@NotNull final XLineBreakpoint breakpoint) {
+ final XSourcePosition position = breakpoint.getSourcePosition();
+ if (position != null) {
+ myDebugProcess.addBreakpoint(myDebugProcess.getPositionConverter().convert(position), breakpoint);
+ myBreakPointPositions.put(breakpoint, position);
+ }
+ }
+
+ public void unregisterBreakpoint(@NotNull final XLineBreakpoint breakpoint, final boolean temporary) {
+ final XSourcePosition position = myBreakPointPositions.get(breakpoint);
+ if (position != null) {
+ myDebugProcess.removeBreakpoint(myDebugProcess.getPositionConverter().convert(position));
+ myBreakPointPositions.remove(breakpoint);
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/debugger/DjangoLineBreakpointHandler.java b/python/src/com/jetbrains/python/debugger/DjangoLineBreakpointHandler.java
new file mode 100644
index 000000000000..eeb76b8ee875
--- /dev/null
+++ b/python/src/com/jetbrains/python/debugger/DjangoLineBreakpointHandler.java
@@ -0,0 +1,10 @@
+package com.jetbrains.python.debugger;
+
+import org.jetbrains.annotations.NotNull;
+
+
+public class DjangoLineBreakpointHandler extends AbstractLineBreakpointHandler {
+ public DjangoLineBreakpointHandler(@NotNull final PyDebugProcess debugProcess) {
+ super(DjangoTemplateLineBreakpointType.class, debugProcess);
+ }
+}
diff --git a/python/src/com/jetbrains/python/debugger/DjangoTemplateLineBreakpointType.java b/python/src/com/jetbrains/python/debugger/DjangoTemplateLineBreakpointType.java
new file mode 100644
index 000000000000..95242531f3ed
--- /dev/null
+++ b/python/src/com/jetbrains/python/debugger/DjangoTemplateLineBreakpointType.java
@@ -0,0 +1,61 @@
+package com.jetbrains.python.debugger;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiWhiteSpace;
+import com.intellij.util.Processor;
+import com.intellij.xdebugger.XDebuggerUtil;
+import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
+import com.intellij.xdebugger.breakpoints.XLineBreakpointType;
+import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
+import com.jetbrains.django.util.DjangoUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+
+public class DjangoTemplateLineBreakpointType extends XLineBreakpointType {
+ private final PyDebuggerEditorsProvider myEditorsProvider = new PyDebuggerEditorsProvider();
+
+ public DjangoTemplateLineBreakpointType() {
+ super("django-line", "Django Line Breakpoint");
+ }
+
+ public boolean canPutAt(@NotNull final VirtualFile file, final int line, @NotNull final Project project) {
+ final Ref stoppable = Ref.create(false);
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document != null) {
+ if (DjangoUtil.isDjangoTemplateDocument(document, project)) {
+ XDebuggerUtil.getInstance().iterateLine(project, document, line, new Processor() {
+ public boolean process(PsiElement psiElement) {
+ if (psiElement instanceof PsiWhiteSpace || psiElement instanceof PsiComment) return true;
+ // Python debugger seems to be able to stop on pretty much everything
+ stoppable.set(true);
+ return false;
+ }
+ });
+ }
+ }
+
+ return stoppable.get();
+ }
+
+ @Nullable
+ public XBreakpointProperties createBreakpointProperties(@NotNull final VirtualFile file, final int line) {
+ return null;
+ }
+
+ @Override
+ public String getBreakpointsDialogHelpTopic() {
+ return "reference.dialogs.breakpoints";
+ }
+
+ @Override
+ public XDebuggerEditorsProvider getEditorsProvider() {
+ return myEditorsProvider;
+ }
+}
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
index 55493a3e7514..811f2ce10731 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
@@ -68,7 +68,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
super(session);
session.setPauseActionSupported(true);
myDebugger = new RemoteDebugger(this, serverSocket, 10);
- myBreakpointHandlers = new XBreakpointHandler[]{new PyLineBreakpointHandler(this), new PyExceptionBreakpointHandler(this)};
+ myBreakpointHandlers = new XBreakpointHandler[]{new PyLineBreakpointHandler(this), new PyExceptionBreakpointHandler(this), new DjangoLineBreakpointHandler(this)};
myEditorsProvider = new PyDebuggerEditorsProvider();
myProcessHandler = processHandler;
myExecutionConsole = executionConsole;
@@ -156,7 +156,8 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
if (!remoteVersion.equals(currentBuild)) {
- printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n", ConsoleViewContentType.ERROR_OUTPUT);
+ printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
+ ConsoleViewContentType.ERROR_OUTPUT);
}
}
@@ -228,7 +229,8 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
dropFrameCaches();
if (myDebugger.isConnected() && !mySuspendedThreads.isEmpty()) {
final PySourcePosition pyPosition = myPositionConverter.convert(position);
- final SetBreakpointCommand command = new SetBreakpointCommand(myDebugger, pyPosition.getFile(), pyPosition.getLine());
+ final SetBreakpointCommand command =
+ new SetBreakpointCommand(myDebugger, PyLineBreakpointType.ID, pyPosition.getFile(), pyPosition.getLine());
myDebugger.execute(command); // set temp. breakpoint
resume(ResumeCommand.Mode.RESUME);
}
@@ -322,17 +324,21 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
myRegisteredBreakpoints.put(position, breakpoint);
if (myDebugger.isConnected()) {
final SetBreakpointCommand command =
- new SetBreakpointCommand(myDebugger, position.getFile(), position.getLine(), breakpoint.getCondition(),
+ new SetBreakpointCommand(myDebugger, breakpoint.getType().getId(), position.getFile(), position.getLine(),
+ breakpoint.getCondition(),
breakpoint.getLogExpression());
myDebugger.execute(command);
}
}
public void removeBreakpoint(final PySourcePosition position) {
- myRegisteredBreakpoints.remove(position);
- if (myDebugger.isConnected()) {
- final RemoveBreakpointCommand command = new RemoveBreakpointCommand(myDebugger, position.getFile(), position.getLine());
- myDebugger.execute(command);
+ XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
+ if (breakpoint != null) {
+ myRegisteredBreakpoints.remove(position);
+ if (myDebugger.isConnected()) {
+ final RemoveBreakpointCommand command = new RemoveBreakpointCommand(myDebugger, breakpoint.getType().getId(), position.getFile(), position.getLine());
+ myDebugger.execute(command);
+ }
}
}
@@ -375,7 +381,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
final PySourcePosition position = frames.get(0).getPosition();
breakpoint = myRegisteredBreakpoints.get(position);
if (breakpoint == null) {
- final RemoveBreakpointCommand command = new RemoveBreakpointCommand(myDebugger, position.getFile(), position.getLine());
+ final RemoveBreakpointCommand command = new RemoveBreakpointCommand(myDebugger, "all", position.getFile(), position.getLine());
myDebugger.execute(command); // remove temp. breakpoint
}
}
diff --git a/python/src/com/jetbrains/python/debugger/PyLineBreakpointHandler.java b/python/src/com/jetbrains/python/debugger/PyLineBreakpointHandler.java
index d94f37677d60..b67230e3f016 100644
--- a/python/src/com/jetbrains/python/debugger/PyLineBreakpointHandler.java
+++ b/python/src/com/jetbrains/python/debugger/PyLineBreakpointHandler.java
@@ -1,49 +1,11 @@
package com.jetbrains.python.debugger;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.intellij.xdebugger.XSourcePosition;
-import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
-import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
-import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
import org.jetbrains.annotations.NotNull;
-import java.util.List;
-import java.util.Map;
-
-public class PyLineBreakpointHandler extends XBreakpointHandler> {
-
- private final PyDebugProcess myDebugProcess;
-
- private final Map, XSourcePosition> myBreakPointPositions = Maps.newHashMap();
+public class PyLineBreakpointHandler extends AbstractLineBreakpointHandler {
public PyLineBreakpointHandler(@NotNull final PyDebugProcess debugProcess) {
- super(PyLineBreakpointType.class);
- myDebugProcess = debugProcess;
- }
-
- public void reregisterBreakpoints() {
- List> breakpoints = Lists.newArrayList(myBreakPointPositions.keySet());
- for (XLineBreakpoint breakpoint : breakpoints) {
- unregisterBreakpoint(breakpoint, false);
- registerBreakpoint(breakpoint);
- }
- }
-
- public void registerBreakpoint(@NotNull final XLineBreakpoint breakpoint) {
- final XSourcePosition position = breakpoint.getSourcePosition();
- if (position != null) {
- myDebugProcess.addBreakpoint(myDebugProcess.getPositionConverter().convert(position), breakpoint);
- myBreakPointPositions.put(breakpoint, position);
- }
- }
-
- public void unregisterBreakpoint(@NotNull final XLineBreakpoint breakpoint, final boolean temporary) {
- final XSourcePosition position = myBreakPointPositions.get(breakpoint);
- if (position != null) {
- myDebugProcess.removeBreakpoint(myDebugProcess.getPositionConverter().convert(position));
- myBreakPointPositions.remove(breakpoint);
- }
+ super(PyLineBreakpointType.class, debugProcess);
}
}
diff --git a/python/src/com/jetbrains/python/debugger/PyLineBreakpointType.java b/python/src/com/jetbrains/python/debugger/PyLineBreakpointType.java
index 825962a1c3b6..0abe87752d94 100644
--- a/python/src/com/jetbrains/python/debugger/PyLineBreakpointType.java
+++ b/python/src/com/jetbrains/python/debugger/PyLineBreakpointType.java
@@ -5,7 +5,9 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.*;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiWhiteSpace;
import com.intellij.util.Processor;
import com.intellij.xdebugger.XDebuggerUtil;
import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
@@ -17,17 +19,20 @@ import org.jetbrains.annotations.Nullable;
public class PyLineBreakpointType extends XLineBreakpointType {
+ public static final String ID = "python-line";
+ private static final String NAME = "Python Line Breakpoint";
+
private final PyDebuggerEditorsProvider myEditorsProvider = new PyDebuggerEditorsProvider();
public PyLineBreakpointType() {
- super("python-line", "Python Line Breakpoint");
+ super(ID, NAME);
}
public boolean canPutAt(@NotNull final VirtualFile file, final int line, @NotNull final Project project) {
final Ref stoppable = Ref.create(false);
- if (file.getFileType() == PythonFileType.INSTANCE) {
- final Document document = FileDocumentManager.getInstance().getDocument(file);
- if (document != null) {
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document != null) {
+ if (file.getFileType() == PythonFileType.INSTANCE) {
XDebuggerUtil.getInstance().iterateLine(project, document, line, new Processor() {
public boolean process(PsiElement psiElement) {
if (psiElement instanceof PsiWhiteSpace || psiElement instanceof PsiComment) return true;
@@ -42,6 +47,7 @@ public class PyLineBreakpointType extends XLineBreakpointType