Merge branch 'master' of git@git.labs.intellij.net:idea/ultimate

This commit is contained in:
Dmitry Cheryasov
2009-12-29 07:35:58 +02:00
91 changed files with 6997 additions and 523 deletions
@@ -1085,5 +1085,6 @@ if __name__ == "__main__":
outfile.close()
except:
sys.stderr.write("Failed to process " + name + " while " + action + "\n")
raise
# Please don't add a 'raise' here. If we fail to process one module, it's not a sufficient reason
# to cancel processing the remaining ones.
continue
File diff suppressed because it is too large Load Diff
+954
View File
@@ -0,0 +1,954 @@
#IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe
from pydevd_constants import * #@UnusedWildImport
from pydevd_comm import CMD_CHANGE_VARIABLE, \
CMD_EVALUATE_EXPRESSION, \
CMD_EXEC_EXPRESSION, \
CMD_GET_FRAME, \
CMD_GET_VARIABLE, \
CMD_LIST_THREADS, \
CMD_REMOVE_BREAK, \
CMD_RUN, \
CMD_SET_BREAK, \
CMD_STEP_INTO, \
CMD_STEP_OVER, \
CMD_STEP_RETURN, \
CMD_THREAD_CREATE, \
CMD_THREAD_KILL, \
CMD_THREAD_RUN, \
CMD_THREAD_SUSPEND, \
CMD_RUN_TO_LINE, \
CMD_RELOAD_CODE, \
CMD_VERSION, \
GetGlobalDebugger, \
InternalChangeVariable, \
InternalEvaluateExpression, \
InternalGetFrame, \
InternalGetVariable, \
InternalTerminateThread, \
NetCommand, \
NetCommandFactory, \
PyDBDaemonThread, \
PydevQueue, \
ReaderThread, \
SetGlobalDebugger, \
WriterThread, \
PydevdFindThreadById, \
PydevdLog, \
StartClient, \
StartServer
import pydevd_file_utils
import traceback
import pydevd_vm_type
import pydevd_tracing
import pydevd_io
import pydevd_additional_thread_info
import time
DONT_TRACE = {
#commonly used things from the stdlib that we don't want to trace
'threading.py':1,
'Queue.py':1,
'socket.py':1,
#things from pydev that we don't want to trace
'pydevd_additional_thread_info.py':1,
'pydevd_comm.py':1,
'pydevd_constants.py':1,
'pydevd_file_utils.py':1,
'pydevd_frame.py':1,
'pydevd_io.py':1 ,
'pydevd_resolver.py':1 ,
'pydevd_tracing.py':1 ,
'pydevd_vars.py':1,
'pydevd_vm_type.py':1,
'pydevd.py':1 ,
'pydevd_psyco_stub.py':1
}
if IS_PY3K:
#if we try to trace io.py it seems it can get halted (see http://bugs.python.org/issue4716)
DONT_TRACE['io.py'] = 1
connected = False
bufferStdOutToServer = False
bufferStdErrToServer = False
PyDBUseLocks = True
#=======================================================================================================================
# PyDBCommandThread
#=======================================================================================================================
class PyDBCommandThread(PyDBDaemonThread):
def __init__(self, pyDb):
PyDBDaemonThread.__init__(self)
self.pyDb = pyDb
self.setName('pydevd.CommandThread')
def OnRun(self):
time.sleep(5) #this one will only start later on (because otherwise we may not have any non-daemon threads
run_traced = True
if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0:
#don't run untraced threads if we're in jython 2.2.1 or lower
#jython bug: if we start a thread and another thread changes the tracing facility
#it affects other threads (it's not set only for the thread but globally)
#Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867
run_traced = False
if run_traced:
pydevd_tracing.SetTrace(None) # no debugging on this thread
try:
while not self.killReceived:
try:
self.pyDb.processInternalCommands()
except:
PydevdLog(0, 'Finishing debug communication...(2)')
time.sleep(0.5)
except:
pass
#only got this error in interpreter shutdown
#PydevdLog(0, 'Finishing debug communication...(3)')
#=======================================================================================================================
# PyDB
#=======================================================================================================================
class PyDB:
""" Main debugging class
Lots of stuff going on here:
PyDB starts two threads on startup that connect to remote debugger (RDB)
The threads continuously read & write commands to RDB.
PyDB communicates with these threads through command queues.
Every RDB command is processed by calling processNetCommand.
Every PyDB net command is sent to the net by posting NetCommand to WriterThread queue
Some commands need to be executed on the right thread (suspend/resume & friends)
These are placed on the internal command queue.
"""
RUNNING_THREAD_IDS = {} #this is a dict of thread ids pointing to thread ids. Whenever a command
#is passed to the java end that acknowledges that a thread was created,
#the thread id should be passed here -- and if at some time we do not find
#that thread alive anymore, we must remove it from this list and make
#the java side know that the thread was killed.
def __init__(self):
SetGlobalDebugger(self)
pydevd_tracing.ReplaceSysSetTraceFunc()
self.reader = None
self.writer = None
self.quitting = None
self.cmdFactory = NetCommandFactory()
self.cmdQueue = {} # the hash of Queues. Key is thread id, value is thread
self.breakpoints = {}
self.readyToRun = False
self.lock = threading.RLock()
self.internalQueueLock = threading.Lock()
self.finishDebuggingSession = False
def acquire(self):
if PyDBUseLocks:
self.lock.acquire()
return True
def release(self):
if PyDBUseLocks:
self.lock.release()
return True
def initializeNetwork(self, sock):
try:
sock.settimeout(None) # infinite, no timeouts from now on - jython does not have it
except:
pass
self.writer = WriterThread(sock)
self.reader = ReaderThread(sock)
self.writer.start()
self.reader.start()
time.sleep(0.1) # give threads time to start
def connect(self, host, port):
if host:
s = StartClient(host, port)
else:
s = StartServer(port)
self.initializeNetwork(s)
def getInternalQueue(self, thread_id):
""" returns intenal command queue for a given thread.
if new queue is created, notify the RDB about it """
try:
return self.cmdQueue[thread_id]
except KeyError:
self.internalQueueLock.acquire()
try:
self.cmdQueue[thread_id] = PydevQueue.Queue()
all_threads = threading.enumerate()
cmd = None
for t in all_threads:
if GetThreadId(t) == thread_id:
if not hasattr(t, 'additionalInfo'):
#see http://sourceforge.net/tracker/index.php?func=detail&aid=1955428&group_id=85796&atid=577329
#Let's create the additional info right away!
t.additionalInfo = pydevd_additional_thread_info.PyDBAdditionalThreadInfo()
self.RUNNING_THREAD_IDS[thread_id] = t
cmd = self.cmdFactory.makeThreadCreatedMessage(t)
break
if cmd:
PydevdLog(2, "found a new thread ", str(thread_id))
self.writer.addCommand(cmd)
else:
PydevdLog(0, "could not find thread by id to register")
finally:
self.internalQueueLock.release()
return self.cmdQueue[thread_id]
def postInternalCommand(self, int_cmd, thread_id):
""" if thread_id is *, post to all """
if thread_id == "*":
for k in self.cmdQueue.keys():
self.cmdQueue[k].put(int_cmd)
else:
queue = self.getInternalQueue(thread_id)
queue.put(int_cmd)
def checkOutput(self, out, outCtx):
'''Checks the output to see if we have to send some buffered output to the debug server
@param out: sys.stdout or sys.stderr
@param outCtx: the context indicating: 1=stdout and 2=stderr (to know the colors to write it)
'''
try:
v = out.getvalue()
if v:
self.cmdFactory.makeIoMessage(v, outCtx, self)
except:
traceback.print_exc()
def processInternalCommands(self):
'''This function processes internal commands
'''
self.acquire()
try:
if bufferStdOutToServer:
self.checkOutput(sys.stdoutBuf, 1) #@UndefinedVariable
if bufferStdErrToServer:
self.checkOutput(sys.stderrBuf, 2) #@UndefinedVariable
currThreadId = GetThreadId(threading.currentThread())
threads = threading.enumerate()
foundNonPyDBDaemonThread = False
foundThreads = {}
for t in threads:
tId = GetThreadId(t)
if t.isAlive():
foundThreads[tId] = tId
if not isinstance(t, PyDBDaemonThread):
foundNonPyDBDaemonThread = True
queue = self.getInternalQueue(GetThreadId(t))
cmdsToReadd = [] #some commands must be processed by the thread itself... if that's the case,
#we will re-add the commands to the queue after executing.
try:
while True:
int_cmd = queue.get(False)
if int_cmd.canBeExecutedBy(currThreadId):
PydevdLog(2, "processing internal command ", str(int_cmd))
int_cmd.doIt(self)
else:
PydevdLog(2, "NOT processing internal command ", str(int_cmd))
cmdsToReadd.append(int_cmd)
except PydevQueue.Empty:
for int_cmd in cmdsToReadd:
queue.put(int_cmd)
# this is how we exit
if not foundNonPyDBDaemonThread:
self.finishDebuggingSession = True
for t in threads:
if hasattr(t, 'doKill'):
t.doKill()
for tId in self.RUNNING_THREAD_IDS.keys():
try:
if not DictContains(foundThreads, tId):
self.processThreadNotAlive(tId)
except:
sys.stderr.write('Error iterating through %s (%s) - %s\n' % (foundThreads, foundThreads.__class__, dir(foundThreads)))
raise
finally:
self.release()
def processNetCommand(self, cmd_id, seq, text):
'''Processes a command received from the Java side
@param cmd_id: the id of the command
@param seq: the sequence of the command
@param text: the text received in the command
@note: this method is run as a big switch... after doing some tests, it's not clear whether changing it for
a dict id --> function call will have better performance result. A simple test with xrange(10000000) showed
that the gains from having a fast access to what should be executed are lost because of the function call in
a way that if we had 10 elements in the switch the if..elif are better -- but growing the number of choices
makes the solution with the dispatch look better -- so, if this gets more than 20-25 choices at some time,
it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before
probably will give better performance).
'''
self.acquire()
try:
try:
cmd = None
if cmd_id == CMD_RUN:
self.readyToRun = True
elif cmd_id == CMD_VERSION:
# response is version number
cmd = self.cmdFactory.makeVersionMessage(seq)
elif cmd_id == CMD_LIST_THREADS:
# response is a list of threads
cmd = self.cmdFactory.makeListThreadsMessage(seq)
elif cmd_id == CMD_THREAD_KILL:
int_cmd = InternalTerminateThread(text)
self.postInternalCommand(int_cmd, text)
elif cmd_id == CMD_THREAD_SUSPEND:
t = PydevdFindThreadById(text)
if t:
additionalInfo = None
try:
additionalInfo = t.additionalInfo
except AttributeError:
pass #that's ok, no info currently set
if additionalInfo is not None:
for frame in additionalInfo.IterFrames():
frame.f_trace = self.trace_dispatch
SetTraceForParents(frame, self.trace_dispatch)
del frame
self.setSuspend(t, CMD_THREAD_SUSPEND)
elif cmd_id == CMD_THREAD_RUN:
t = PydevdFindThreadById(text)
if t:
t.additionalInfo.pydev_step_cmd = None
t.additionalInfo.pydev_step_stop = None
t.additionalInfo.pydev_state = STATE_RUN
elif cmd_id == CMD_STEP_INTO or cmd_id == CMD_STEP_OVER or cmd_id == CMD_STEP_RETURN:
#we received some command to make a single step
t = PydevdFindThreadById(text)
if t:
t.additionalInfo.pydev_step_cmd = cmd_id
t.additionalInfo.pydev_state = STATE_RUN
elif cmd_id == CMD_RUN_TO_LINE:
#we received some command to make a single step
thread_id, line, func_name = text.split('\t', 2)
t = PydevdFindThreadById(thread_id)
if t:
t.additionalInfo.pydev_step_cmd = cmd_id
t.additionalInfo.pydev_next_line = int(line)
t.additionalInfo.pydev_func_name = func_name
t.additionalInfo.pydev_state = STATE_RUN
elif cmd_id == CMD_RELOAD_CODE:
#we received some command to make a reload of a module
module_name = text.strip()
from pydevd_reload import xreload
if not DictContains(sys.modules, module_name):
if '.' in module_name:
new_module_name = module_name.split('.')[-1]
if DictContains(sys.modules, new_module_name):
module_name = new_module_name
if not DictContains(sys.modules, module_name):
sys.stderr.write('pydev debugger: Unable to find module to reload: "'+module_name+'".\n')
sys.stderr.write('pydev debugger: This usually means you are trying to reload the __main__ module (which cannot be reloaded).\n')
else:
sys.stderr.write('pydev debugger: Reloading: '+module_name+'\n')
xreload(sys.modules[module_name])
elif cmd_id == CMD_CHANGE_VARIABLE:
#the text is: thread\tstackframe\tFRAME|GLOBAL\tattribute_to_change\tvalue_to_change
try:
thread_id, frame_id, scope, attr_and_value = text.split('\t', 3)
tab_index = attr_and_value.rindex('\t')
attr = attr_and_value[0:tab_index].replace('\t', '.')
value = attr_and_value[tab_index + 1:]
int_cmd = InternalChangeVariable(seq, thread_id, frame_id, scope, attr, value)
self.postInternalCommand(int_cmd, thread_id)
except:
traceback.print_exc()
elif cmd_id == CMD_GET_VARIABLE:
#we received some command to get a variable
#the text is: thread_id\tframe_id\tFRAME|GLOBAL\tattributes*
try:
thread_id, frame_id, scopeattrs = text.split('\t', 2)
if scopeattrs.find('\t') != -1: # there are attibutes beyond scope
scope, attrs = scopeattrs.split('\t', 1)
else:
scope, attrs = (scopeattrs, None)
int_cmd = InternalGetVariable(seq, thread_id, frame_id, scope, attrs)
self.postInternalCommand(int_cmd, thread_id)
except:
traceback.print_exc()
elif cmd_id == CMD_GET_FRAME:
thread_id, frame_id, scope = text.split('\t', 2)
int_cmd = InternalGetFrame(seq, thread_id, frame_id)
self.postInternalCommand(int_cmd, thread_id)
elif cmd_id == CMD_SET_BREAK:
#func name: 'None': match anything. Empty: match global, specified: only method context.
#command to add some breakpoint.
# text is file\tline. Add to breakpoints dictionary
file, line, condition = text.split('\t', 2)
if condition.startswith('**FUNC**'):
func_name, condition = condition.split('\t', 1)
#We must restore new lines and tabs as done in
#AbstractDebugTarget.breakpointAdded
condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\
replace("@_@TAB_CHAR@_@", '\t').strip()
func_name = func_name[8:]
else:
func_name = 'None' #Match anything if not specified.
file = pydevd_file_utils.NormFileToServer(file)
if not os.path.exists(file):
sys.stderr.write('pydev debugger: warning: trying to add breakpoint'\
' to file that does not exist: %s (will have no effect)\n' % (file,))
line = int(line)
if DEBUG_TRACE_BREAKPOINTS > 0:
sys.stderr.write('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
if DictContains(self.breakpoints, file):
breakDict = self.breakpoints[file]
else:
breakDict = {}
if len(condition) <= 0 or condition == None or condition == "None":
breakDict[line] = (True, None, func_name)
else:
breakDict[line] = (True, condition, func_name)
self.breakpoints[file] = breakDict
#and enable the tracing for existing threads (because there may be frames being executed that
#are currently untraced).
threads = threading.enumerate()
for t in threads:
if not t.getName().startswith('pydevd.'):
#TODO: optimize so that we only actually add that tracing if it's in
#the new breakpoint context.
additionalInfo = None
try:
additionalInfo = t.additionalInfo
except AttributeError:
pass #that's ok, no info currently set
if additionalInfo is not None:
for frame in additionalInfo.IterFrames():
frame.f_trace = self.trace_dispatch
SetTraceForParents(frame, self.trace_dispatch)
del frame
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)
file = pydevd_file_utils.NormFileToServer(file)
try:
line = int(line)
except ValueError:
pass
else:
try:
del self.breakpoints[file][line] #remove the breakpoint in that line
if DEBUG_TRACE_BREAKPOINTS > 0:
sys.stderr.write('Removed breakpoint:%s\n' % (file,))
except KeyError:
#ok, it's not there...
if DEBUG_TRACE_BREAKPOINTS > 0:
#Sometimes, when adding a breakpoint, it adds a remove command before (don't really know why)
sys.stderr.write("breakpoint not found: %s - %s\n" % (file, line))
elif cmd_id == CMD_EVALUATE_EXPRESSION or cmd_id == CMD_EXEC_EXPRESSION:
#command to evaluate the given expression
#text is: thread\tstackframe\tLOCAL\texpression
thread_id, frame_id, scope, expression = text.split('\t', 3)
int_cmd = InternalEvaluateExpression(seq, thread_id, frame_id, expression,
cmd_id == CMD_EXEC_EXPRESSION)
self.postInternalCommand(int_cmd, thread_id)
else:
#I have no idea what this is all about
cmd = self.cmdFactory.makeErrorMessage(seq, "unexpected command " + str(cmd_id))
if cmd is not None:
self.writer.addCommand(cmd)
del cmd
except Exception:
traceback.print_exc()
cmd = self.cmdFactory.makeErrorMessage(seq,
"Unexpected exception in processNetCommand.\nInitial params: %s" % ((cmd_id, seq, text),))
self.writer.addCommand(cmd)
finally:
self.release()
def processThreadNotAlive(self, threadId):
""" if thread is not alive, cancel trace_dispatch processing """
thread = self.RUNNING_THREAD_IDS.get(threadId, None)
if thread is None:
return
del self.RUNNING_THREAD_IDS[threadId]
wasNotified = thread.additionalInfo.pydev_notify_kill
if not wasNotified:
cmd = self.cmdFactory.makeThreadKilledMessage(threadId)
self.writer.addCommand(cmd)
thread.additionalInfo.pydev_notify_kill = True
def setSuspend(self, thread, stop_reason):
thread.additionalInfo.pydev_state = STATE_SUSPEND
thread.stop_reason = stop_reason
def doWaitSuspend(self, thread, frame, event, arg): #@UnusedVariable
""" busy waits until the thread state changes to RUN
it expects thread's state as attributes of the thread.
Upon running, processes any outstanding Stepping commands.
"""
self.processInternalCommands()
cmd = self.cmdFactory.makeThreadSuspendMessage(GetThreadId(thread), frame, thread.stop_reason)
self.writer.addCommand(cmd)
info = thread.additionalInfo
while info.pydev_state == STATE_SUSPEND and not self.finishDebuggingSession:
self.processInternalCommands()
time.sleep(0.2)
#process any stepping instructions
if info.pydev_step_cmd == CMD_STEP_INTO:
info.pydev_step_stop = None
elif info.pydev_step_cmd == CMD_STEP_OVER:
info.pydev_step_stop = frame
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
SetTraceForParents(frame, self.trace_dispatch)
elif info.pydev_step_cmd == CMD_RUN_TO_LINE:
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
SetTraceForParents(frame, self.trace_dispatch)
if event == 'line':
#If we're already in the correct context, we have to stop it now, because we can act only on
#line events -- if a return was the next statement it wouldn't work (so, we have this code
#repeated at pydevd_frame).
stop = False
curr_func_name = frame.f_code.co_name
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
if curr_func_name == info.pydev_func_name:
line = info.pydev_next_line
if frame.f_lineno == line:
stop = True
else:
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
frame.f_lineno = line
frame.f_trace = None
stop = True
if stop:
info.pydev_state = STATE_SUSPEND
self.doWaitSuspend(thread, frame, event, arg)
return
elif info.pydev_step_cmd == CMD_STEP_RETURN:
back_frame = frame.f_back
if back_frame is not None:
#steps back to the same frame (in a return call it will stop in the 'back frame' for the user)
info.pydev_step_stop = frame
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
SetTraceForParents(frame, self.trace_dispatch)
else:
#No back frame?!? -- this happens in jython when we have some frame created from an awt event
#(the previous frame would be the awt event, but this doesn't make part of 'jython', only 'java')
#so, if we're doing a step return in this situation, it's the same as just making it run
info.pydev_step_stop = None
info.pydev_step_cmd = None
info.pydev_state = STATE_RUN
del frame
cmd = self.cmdFactory.makeThreadRunMessage(GetThreadId(thread), info.pydev_step_cmd)
self.writer.addCommand(cmd)
def trace_dispatch(self, frame, event, arg):
''' This is the callback used when we enter some context in the debugger.
We also decorate the thread we are in with info about the debugging.
The attributes added are:
pydev_state
pydev_step_stop
pydev_step_cmd
pydev_notify_kill
'''
try:
if self.finishDebuggingSession:
#that was not working very well because jython gave some socket errors
threads = threading.enumerate()
for t in threads:
if hasattr(t, 'doKill'):
t.doKill()
return None
filename, base = pydevd_file_utils.GetFilenameAndBase(frame)
if DictContains(DONT_TRACE, base): #we don't want to debug threading or anything related to pydevd
return None
#print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name)
try:
#this shouldn't give an exception, but it could happen... (python bug)
#see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html
#and related bug: http://bugs.python.org/issue1733757
t = threading.currentThread()
except:
frame.f_trace = self.trace_dispatch
return self.trace_dispatch
# if thread is not alive, cancel trace_dispatch processing
if not t.isAlive():
self.processThreadNotAlive(GetThreadId(t))
return None # suspend tracing
try:
additionalInfo = t.additionalInfo
except AttributeError:
t.additionalInfo = additionalInfo = pydevd_additional_thread_info.PyDBAdditionalThreadInfo()
#always keep a reference to the topmost frame so that we're able to start tracing it (if it was untraced)
#that's needed when a breakpoint is added in a current frame for a currently untraced context.
#each new frame...
dbFrame = additionalInfo.CreateDbFrame(self, filename, additionalInfo, t, frame)
return dbFrame.trace_dispatch(frame, event, arg)
except SystemExit:
return None
except Exception:
#Log it
traceback.print_exc()
return None
if USE_PSYCO_OPTIMIZATION:
try:
import psyco
trace_dispatch = psyco.proxy(trace_dispatch)
processNetCommand = psyco.proxy(processNetCommand)
processInternalCommands = psyco.proxy(processInternalCommands)
doWaitSuspend = psyco.proxy(doWaitSuspend)
getInternalQueue = psyco.proxy(getInternalQueue)
except ImportError:
if hasattr(sys, 'exc_clear'): #jython does not have it
sys.exc_clear() #don't keep the traceback (let's keep it clear for when we go to the point of executing client code)
if not sys.platform.startswith("java") and not sys.platform.startswith("cli"):
sys.stderr.write("pydev debugger: warning: psyco not available for speedups (the debugger will still work correctly, but a bit slower)\n")
def run(self, file, globals=None, locals=None):
if globals is None:
#patch provided by: Scott Schlesier - when script is run, it does not
#use globals from pydevd:
#This will prevent the pydevd script from contaminating the namespace for the script to be debugged
#pretend pydevd is not the main module, and
#convince the file to be debugged that it was loaded as main
sys.modules['pydevd'] = sys.modules['__main__']
sys.modules['pydevd'].__name__ = 'pydevd'
from imp import new_module
m = new_module('__main__')
sys.modules['__main__'] = m
m.__file__ = file
globals = m.__dict__
if locals is None:
locals = globals
#Predefined (writable) attributes: __name__ is the module's name;
#__doc__ is the module's documentation string, or None if unavailable;
#__file__ is the pathname of the file from which the module was loaded,
#if it was loaded from a file. The __file__ attribute is not present for
#C modules that are statically linked into the interpreter; for extension modules
#loaded dynamically from a shared library, it is the pathname of the shared library file.
#I think this is an ugly hack, bug it works (seems to) for the bug that says that sys.path should be the same in
#debug and run.
if m.__file__.startswith(sys.path[0]):
#print >> sys.stderr, 'Deleting: ', sys.path[0]
del sys.path[0]
#now, the local directory has to be added to the pythonpath
#sys.path.insert(0, os.getcwd())
#Changed: it's not the local directory, but the directory of the file launched
#The file being run ust be in the pythonpath (even if it was not before)
sys.path.insert(0, os.path.split(file)[0])
# for completness, we'll register the pydevd.reader & pydevd.writer threads
net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.reader" id="-1"/></xml>')
self.writer.addCommand(net)
net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.writer" id="-1"/></xml>')
self.writer.addCommand(net)
pydevd_tracing.SetTrace(self.trace_dispatch)
try:
#not available in jython!
threading.settrace(self.trace_dispatch) # for all future threads
except:
pass
while not self.readyToRun:
time.sleep(0.1) # busy wait until we receive run command
PyDBCommandThread(debugger).start()
if not IS_PY3K:
execfile(file, globals, locals) #execute the script
else:
#We need to compile before so that the module name is correct
obj = compile(open(file).read()+"\n", file, 'exec')
exec(obj, globals, locals) #execute the script
def processCommandLine(argv):
""" parses the arguments.
removes our arguments from the command line """
retVal = {}
retVal['client'] = ''
retVal['server'] = False
retVal['port'] = 0
retVal['file'] = ''
i = 0
del argv[0]
while (i < len(argv)):
if (argv[i] == '--port'):
del argv[i]
retVal['port'] = int(argv[i])
del argv[i]
elif (argv[i] == '--vm_type'):
del argv[i]
retVal['vm_type'] = argv[i]
del argv[i]
elif (argv[i] == '--client'):
del argv[i]
retVal['client'] = argv[i]
del argv[i]
elif (argv[i] == '--server'):
del argv[i]
retVal['server'] = True
elif (argv[i] == '--file'):
del argv[i]
retVal['file'] = argv[i];
i = len(argv) # pop out, file is our last argument
elif (argv[i] == '--DEBUG_RECORD_SOCKET_READS'):
del argv[i]
retVal['DEBUG_RECORD_SOCKET_READS'] = True
else:
raise ValueError("unexpected option " + argv[i])
return retVal
def usage(doExit=0):
sys.stdout.write('Usage:\n')
sys.stdout.write('pydevd.py --port=N [(--client hostname) | --server] --file executable [file_options]\n')
if doExit:
sys.exit(0)
def SetTraceForParents(frame, dispatch_func):
frame = frame.f_back
while frame:
if frame.f_trace is None:
frame.f_trace = dispatch_func
frame = frame.f_back
del frame
def settrace(host='localhost', stdoutToServer=False, stderrToServer=False, port=5678, suspend=True):
'''Sets the tracing function with the pydev debug function and initializes needed facilities.
@param host: the user may specify another host, if the debug server is not in the same machine
@param stdoutToServer: when this is true, the stdout is passed to the debug server
@param stderrToServer: when this is true, the stderr is passed to the debug server
so that they are printed in its console and not in this process console.
@param port: specifies which port to use for communicating with the server (note that the server must be started
in the same port). @note: currently it's hard-coded at 5678 in the client
@param suspend: whether a breakpoint should be emulated as soon as this function is called.
'''
global connected
global bufferStdOutToServer
global bufferStdErrToServer
if not connected :
connected = True
bufferStdOutToServer = stdoutToServer
bufferStdErrToServer = stderrToServer
pydevd_vm_type.SetupType()
debugger = PyDB()
debugger.connect(host, port)
net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.reader" id="-1"/></xml>')
debugger.writer.addCommand(net)
net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.writer" id="-1"/></xml>')
debugger.writer.addCommand(net)
if bufferStdOutToServer:
sys.stdoutBuf = pydevd_io.IOBuf()
sys.stdout = pydevd_io.IORedirector(sys.stdout, sys.stdoutBuf) #@UndefinedVariable
if bufferStdErrToServer:
sys.stderrBuf = pydevd_io.IOBuf()
sys.stderr = pydevd_io.IORedirector(sys.stderr, sys.stderrBuf) #@UndefinedVariable
SetTraceForParents(GetFrame(), debugger.trace_dispatch)
t = threading.currentThread()
try:
additionalInfo = t.additionalInfo
except AttributeError:
additionalInfo = pydevd_additional_thread_info.PyDBAdditionalThreadInfo()
t.additionalInfo = additionalInfo
while not debugger.readyToRun:
time.sleep(0.1) # busy wait until we receive run command
if suspend:
debugger.setSuspend(t, CMD_SET_BREAK)
#that's right, debug only threads that pass through this function
#(so, we just call sys.settrace and not threading.settrace)
#note that we do that through pydevd_tracing.SetTrace so that the tracing
#is not warned to the user!
pydevd_tracing.SetTrace(debugger.trace_dispatch)
PyDBCommandThread(debugger).start()
else:
#ok, we're already in debug mode, with all set, so, let's just set the break
debugger = GetGlobalDebugger()
SetTraceForParents(GetFrame(), debugger.trace_dispatch)
t = threading.currentThread()
try:
additionalInfo = t.additionalInfo
except AttributeError:
additionalInfo = pydevd_additional_thread_info.PyDBAdditionalThreadInfo()
t.additionalInfo = additionalInfo
pydevd_tracing.SetTrace(debugger.trace_dispatch)
if suspend:
debugger.setSuspend(t, CMD_SET_BREAK)
if __name__ == '__main__':
sys.stderr.write("pydev debugger: starting\n")
# parse the command line. --file is our last argument that is required
try:
setup = processCommandLine(sys.argv)
except ValueError:
traceback.print_exc()
usage(1)
#as to get here all our imports are already resolved, the psyco module can be
#changed and we'll still get the speedups in the debugger, as those functions
#are already compiled at this time.
try:
import psyco
except ImportError:
if hasattr(sys, 'exc_clear'): #jython does not have it
sys.exc_clear() #don't keep the traceback -- clients don't want to see it
pass #that's ok, no need to mock psyco if it's not available anyways
else:
#if it's available, let's change it for a stub (pydev already made use of it)
import pydevd_psyco_stub
sys.modules['psyco'] = pydevd_psyco_stub
PydevdLog(2, "Executing file ", setup['file'])
PydevdLog(2, "arguments:", str(sys.argv))
pydevd_vm_type.SetupType(setup.get('vm_type', None))
DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = setup.get('DEBUG_RECORD_SOCKET_READS', False)
debugger = PyDB()
debugger.connect(setup['client'], setup['port'])
debugger.run(setup['file'], None, None)
@@ -0,0 +1,138 @@
import sys
from pydevd_constants import * #@UnusedWildImport
import threading
from pydevd_frame import PyDBFrame
import weakref
#=======================================================================================================================
# AbstractPyDBAdditionalThreadInfo
#=======================================================================================================================
class AbstractPyDBAdditionalThreadInfo:
def __init__(self):
self.pydev_state = STATE_RUN
self.pydev_step_stop = None
self.pydev_step_cmd = None
self.pydev_notify_kill = False
def IterFrames(self):
raise NotImplementedError()
def CreateDbFrame(self, mainDebugger, filename, base, additionalInfo, t, frame):
raise NotImplementedError()
def __str__(self):
return 'State:%s Stop:%s Cmd: %s Kill:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill)
#=======================================================================================================================
# PyDBAdditionalThreadInfoWithCurrentFramesSupport
#=======================================================================================================================
class PyDBAdditionalThreadInfoWithCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
def IterFrames(self):
#sys._current_frames(): dictionary with thread id -> topmost frame
return sys._current_frames().values() #return a copy... don't know if it's changed if we did get an iterator
#just create the db frame directly
CreateDbFrame = PyDBFrame
#=======================================================================================================================
# PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
#=======================================================================================================================
class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
def __init__(self):
AbstractPyDBAdditionalThreadInfo.__init__(self)
#That's where the last frame entered is kept. That's needed so that we're able to
#trace contexts that were previously untraced and are currently active. So, the bad thing
#is that the frame may be kept alive longer than it would if we go up on the frame stack,
#and is only disposed when some other frame is removed.
#A better way would be if we could get the topmost frame for each thread, but that's
#not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfoWithCurrentFramesSupport version)
#Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html)
#NOT RLock!! (could deadlock if it was)
self.lock = threading.Lock()
self._acquire_lock = self.lock.acquire
self._release_lock = self.lock.release
#collection with the refs
d = {}
self.pydev_existing_frames = d
try:
self._iter_frames = d.iterkeys
except AttributeError:
self._iter_frames = d.keys
def _OnDbFrameCollected(self, ref):
'''
Callback to be called when a given reference is garbage-collected.
'''
self._acquire_lock()
try:
del self.pydev_existing_frames[ref]
finally:
self._release_lock()
def _AddDbFrame(self, db_frame):
self._acquire_lock()
try:
#create the db frame with a callback to remove it from the dict when it's garbage-collected
#(could be a set, but that's not available on all versions we want to target).
r = weakref.ref(db_frame, self._OnDbFrameCollected)
self.pydev_existing_frames[r] = r
finally:
self._release_lock()
def CreateDbFrame(self, mainDebugger, filename, additionalInfo, t, frame):
#the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept
#alive until its trace_dispatch method is not referenced anymore).
#that's a large workaround because:
#1. we can't have weak-references to python frame object
#2. only from 2.5 onwards we have _current_frames support from the interpreter
db_frame = PyDBFrame(mainDebugger, filename, additionalInfo, t, frame)
db_frame.frame = frame
self._AddDbFrame(db_frame)
return db_frame
def IterFrames(self):
#We cannot use yield (because of the lock)
self._acquire_lock()
try:
ret = []
for weak_db_frame in self._iter_frames():
try:
ret.append(weak_db_frame().frame)
except AttributeError:
pass #ok, garbage-collected already
return ret
finally:
self._release_lock()
def __str__(self):
return 'State:%s Stop:%s Cmd: %s Kill:%s Frames:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill, len(self.IterFrames()))
#=======================================================================================================================
# NOW, WE HAVE TO DEFINE WHICH THREAD INFO TO USE
# (whether we have to keep references to the frames or not)
# from version 2.5 onwards, we can use sys._current_frames to get a dict with the threads
# and frames, but to support other versions, we can't rely on that.
#=======================================================================================================================
if hasattr(sys, '_current_frames'):
PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithCurrentFramesSupport
else:
try:
import threadframe
sys._current_frames = threadframe.dict
assert sys._current_frames is threadframe.dict #Just check if it was correctly set
PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithCurrentFramesSupport
except:
#If all fails, let's use the support without frames
PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
+694
View File
@@ -0,0 +1,694 @@
''' pydevd - a debugging daemon
This is the daemon you launch for python remote debugging.
Protocol:
each command has a format:
id\tsequence-num\ttext
id: protocol command number
sequence-num: each request has a sequence number. Sequence numbers
originating at the debugger are odd, sequence numbers originating
at the daemon are even. Every response uses the same sequence number
as the request.
payload: it is protocol dependent. When response is a complex structure, it
is returned as XML. Each attribute value is urlencoded, and then the whole
payload is urlencoded again to prevent stray characters corrupting protocol/xml encodings
Commands:
NUMBER NAME FROM* ARGUMENTS RESPONSE NOTE
100 series: program execution
101 RUN JAVA - -
102 LIST_THREADS JAVA RETURN with XML listing of all threads
103 THREAD_CREATE PYDB - XML with thread information
104 THREAD_KILL JAVA id (or * to exit) kills the thread
PYDB id nofies JAVA that thread was killed
105 THREAD_SUSPEND JAVA XML of the stack, suspends the thread
reason for suspension
PYDB id notifies JAVA that thread was suspended
106 CMD_THREAD_RUN JAVA id resume the thread
PYDB id \t reason notifies JAVA that thread was resumed
107 STEP_INTO JAVA thread_id
108 STEP_OVER JAVA thread_id
109 STEP_RETURN JAVA thread_id
110 GET_VARIABLE JAVA thread_id \t frame_id \t GET_VARIABLE with XML of var content
FRAME|GLOBAL \t attributes*
111 SET_BREAK JAVA file/line of the breakpoint
112 REMOVE_BREAK JAVA file/line of the return
113 CMD_EVALUATE_EXPRESSION JAVA expression result of evaluating the expression
114 CMD_GET_FRAME JAVA request for frame contents
115 CMD_EXEC_EXPRESSION JAVA
116 CMD_WRITE_TO_CONSOLE PYDB
500 series diagnostics/ok
901 VERSION either Version string (1.0) Currently just used at startup
902 RETURN either Depends on caller -
900 series: errors
501 ERROR either - This is reserved for unexpected errors.
* JAVA - remote debugger, the java end
* PYDB - pydevd, the python end
'''
from pydevd_constants import * #@UnusedWildImport
import time
import threading
try:
import Queue as PydevQueue
except ImportError:
import queue as PydevQueue
from socket import socket
from socket import AF_INET, SOCK_STREAM
try:
from urllib import quote
except:
from urllib.parse import quote
import pydevd_vars
import pydevd_tracing
import pydevd_vm_type
import pydevd_file_utils
import traceback
from pydevd_tracing import GetExceptionTracebackStr
CMD_RUN = 101
CMD_LIST_THREADS = 102
CMD_THREAD_CREATE = 103
CMD_THREAD_KILL = 104
CMD_THREAD_SUSPEND = 105
CMD_THREAD_RUN = 106
CMD_STEP_INTO = 107
CMD_STEP_OVER = 108
CMD_STEP_RETURN = 109
CMD_GET_VARIABLE = 110
CMD_SET_BREAK = 111
CMD_REMOVE_BREAK = 112
CMD_EVALUATE_EXPRESSION = 113
CMD_GET_FRAME = 114
CMD_EXEC_EXPRESSION = 115
CMD_WRITE_TO_CONSOLE = 116
CMD_CHANGE_VARIABLE = 117
CMD_RUN_TO_LINE = 118
CMD_RELOAD_CODE = 119
CMD_VERSION = 501
CMD_RETURN = 502
CMD_ERROR = 901
ID_TO_MEANING = {
'101':'CMD_RUN',
'102':'CMD_LIST_THREADS',
'103':'CMD_THREAD_CREATE',
'104':'CMD_THREAD_KILL',
'105':'CMD_THREAD_SUSPEND',
'106':'CMD_THREAD_RUN',
'107':'CMD_STEP_INTO',
'108':'CMD_STEP_OVER',
'109':'CMD_STEP_RETURN',
'110':'CMD_GET_VARIABLE',
'111':'CMD_SET_BREAK',
'112':'CMD_REMOVE_BREAK',
'113':'CMD_EVALUATE_EXPRESSION',
'114':'CMD_GET_FRAME',
'115':'CMD_EXEC_EXPRESSION',
'116':'CMD_WRITE_TO_CONSOLE',
'117':'CMD_CHANGE_VARIABLE',
'118':'CMD_RUN_TO_LINE',
'119':'CMD_RELOAD_CODE',
'501':'CMD_VERSION',
'502':'CMD_RETURN',
'901':'CMD_ERROR',
}
MAX_IO_MSG_SIZE = 1000 #if the io is too big, we'll not send all (could make the debugger too non-responsive)
#this number can be changed if there's need to do so
VERSION_STRING = "1.1"
#--------------------------------------------------------------------------------------------------- UTILITIES
#=======================================================================================================================
# PydevdLog
#=======================================================================================================================
def PydevdLog(level, *args):
""" levels are:
0 most serious warnings/errors
1 warnings/significant events
2 informational trace
"""
if level <= DEBUG_TRACE_LEVEL:
#yes, we can have errors printing if the console of the program has been finished (and we're still trying to print something)
try:
sys.stderr.write('%s\n' % (args,))
except:
pass
#=======================================================================================================================
# GlobalDebuggerHolder
#=======================================================================================================================
class GlobalDebuggerHolder:
'''
Holder for the global debugger.
'''
globalDbg = None
#=======================================================================================================================
# GetGlobalDebugger
#=======================================================================================================================
def GetGlobalDebugger():
return GlobalDebuggerHolder.globalDbg
#=======================================================================================================================
# SetGlobalDebugger
#=======================================================================================================================
def SetGlobalDebugger(dbg):
GlobalDebuggerHolder.globalDbg = dbg
#------------------------------------------------------------------- ACTUAL COMM
#=======================================================================================================================
# PyDBDaemonThread
#=======================================================================================================================
class PyDBDaemonThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.setDaemon(True)
self.killReceived = False
def run(self):
if sys.platform.startswith("java"):
import org.python.core as PyCore #@UnresolvedImport
ss = PyCore.PySystemState()
# Note: Py.setSystemState() affects only the current thread.
PyCore.Py.setSystemState(ss)
self.OnRun()
def OnRun(self):
raise NotImplementedError('Should be reimplemented by: %s' % self.__class__)
def doKill(self):
#that was not working very well because jython gave some socket errors
self.killReceived = True
#=======================================================================================================================
# ReaderThread
#=======================================================================================================================
class ReaderThread(PyDBDaemonThread):
""" reader thread reads and dispatches commands in an infinite loop """
def __init__(self, sock):
PyDBDaemonThread.__init__(self)
self.sock = sock
self.setName("pydevd.Reader")
def doKill(self):
#We must close the socket so that it doesn't stay halted there.
self.killReceived = True
try:
self.sock.close()
except:
#just ignore that
pass
def OnRun(self):
pydevd_tracing.SetTrace(None) # no debugging on this thread
buffer = ""
try:
while not self.killReceived:
try:
r = self.sock.recv(1024)
except:
GlobalDebuggerHolder.globalDbg.finishDebuggingSession = True
break #Finished communication.
if IS_PY3K:
r = r.decode('utf-8')
buffer += r
if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS:
sys.stdout.write('received >>%s<<\n' % (buffer,))
if len(buffer) == 0:
GlobalDebuggerHolder.globalDbg.finishDebuggingSession = True
break
while buffer.find('\n') != -1:
command, buffer = buffer.split('\n', 1)
PydevdLog(1, "received command ", command)
args = command.split('\t', 2)
GlobalDebuggerHolder.globalDbg.processNetCommand(int(args[0]), int(args[1]), args[2])
except:
traceback.print_exc()
GlobalDebuggerHolder.globalDbg.finishDebuggingSession = True
#----------------------------------------------------------------------------------- SOCKET UTILITIES - WRITER
#=======================================================================================================================
# WriterThread
#=======================================================================================================================
class WriterThread(PyDBDaemonThread):
""" writer thread writes out the commands in an infinite loop """
def __init__(self, sock):
PyDBDaemonThread.__init__(self)
self.sock = sock
self.setName("pydevd.Writer")
self.cmdQueue = PydevQueue.Queue()
if pydevd_vm_type.GetVmType() == 'python':
self.timeout = 0
else:
self.timeout = 0.1
def addCommand(self, cmd):
""" cmd is NetCommand """
self.cmdQueue.put(cmd)
def OnRun(self):
""" just loop and write responses """
pydevd_tracing.SetTrace(None) # no debugging on this thread
try:
while not self.killReceived:
try:
cmd = self.cmdQueue.get(1)
except:
#PydevdLog(0, 'Finishing debug communication...(1)')
#when liberating the thread here, we could have errors because we were shutting down
#but the thread was still not liberated
return
out = cmd.getOutgoing()
if DEBUG_TRACE_LEVEL >= 1:
out_message = 'sending cmd: '
out_message += ID_TO_MEANING.get(out[:3], 'UNKNOWN')
out_message += ' '
out_message += out
try:
sys.stderr.write('%s\n' % (out_message,))
except:
pass
if IS_PY3K:
out = bytearray(out, 'utf-8')
self.sock.send(out) #TODO: this does not guarantee that all message are sent (and jython does not have a send all)
if time is None:
break #interpreter shutdown
time.sleep(self.timeout)
except Exception:
GlobalDebuggerHolder.globalDbg.finishDebuggingSession = True
if DEBUG_TRACE_LEVEL >= 0:
traceback.print_exc()
#--------------------------------------------------- CREATING THE SOCKET THREADS
#=======================================================================================================================
# StartServer
#=======================================================================================================================
def StartServer(port):
""" binds to a port, waits for the debugger to connect """
s = socket(AF_INET, SOCK_STREAM)
s.bind(('', port))
s.listen(1)
newSock, _addr = s.accept()
return newSock
#=======================================================================================================================
# StartClient
#=======================================================================================================================
def StartClient(host, port):
""" connects to a host/port """
PydevdLog(1, "Connecting to ", host, ":", str(port))
try:
s = socket(AF_INET, SOCK_STREAM);
s.connect((host, port))
PydevdLog(1, "Connected.")
return s
except:
sys.stderr.write("server timed out after 10 seconds, could not connect to %s: %s\n" % (host, port))
sys.stderr.write("Exiting. Bye!\n")
sys.exit(1)
#------------------------------------------------------------------------------------ MANY COMMUNICATION STUFF
#=======================================================================================================================
# NetCommand
#=======================================================================================================================
class NetCommand:
""" Commands received/sent over the network.
Command can represent command received from the debugger,
or one to be sent by daemon.
"""
next_seq = 0 # sequence numbers
def __init__(self, id, seq, text):
""" smart handling of paramaters
if sequence is 0, new sequence will be generated
if text has carriage returns they'll be replaced"""
self.id = id
if (seq == 0): seq = self.getNextSeq()
self.seq = seq
self.text = text
self.outgoing = self.makeMessage(id, seq, text)
def getNextSeq(self):
""" returns next sequence number """
NetCommand.next_seq += 2
return NetCommand.next_seq
def getOutgoing(self):
""" returns the outgoing message"""
return self.outgoing
def makeMessage(self, cmd, seq, payload):
encoded = quote(str(payload), '/<>_=" \t')
return str(cmd) + '\t' + str(seq) + '\t' + encoded + "\n"
#=======================================================================================================================
# NetCommandFactory
#=======================================================================================================================
class NetCommandFactory:
def __init_(self):
self.next_seq = 0
def threadToXML(self, thread):
""" thread information as XML """
name = pydevd_vars.makeValidXmlValue(thread.getName())
cmdText = '<thread name="%s" id="%s" />' % (quote(name), GetThreadId(thread))
return cmdText
def makeErrorMessage(self, seq, text):
cmd = NetCommand(CMD_ERROR, seq, text)
if DEBUG_TRACE_LEVEL > 2:
sys.stderr.write("Error: %s" % (text,))
return cmd;
def makeThreadCreatedMessage(self, thread):
cmdText = "<xml>" + self.threadToXML(thread) + "</xml>"
return NetCommand(CMD_THREAD_CREATE, 0, cmdText)
def makeListThreadsMessage(self, seq):
""" returns thread listing as XML """
try:
t = threading.enumerate()
cmdText = "<xml>"
for i in t:
if t.isAlive():
cmdText += self.threadToXML(i)
cmdText += "</xml>"
return NetCommand(CMD_RETURN, seq, cmdText)
except:
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
def makeIoMessage(self, v, ctx, dbg=None):
'''
@param v: the message to pass to the debug server
@param ctx: 1 for stdio 2 for stderr
@param dbg: If not none, add to the writer
'''
try:
if len(v) > MAX_IO_MSG_SIZE:
v = v[0:MAX_IO_MSG_SIZE]
v += '...'
v = pydevd_vars.makeValidXmlValue(quote(v, '/>_= \t'))
net = NetCommand(str(CMD_WRITE_TO_CONSOLE), 0, '<xml><io s="%s" ctx="%s"/></xml>' % (v, ctx))
if dbg:
dbg.writer.addCommand(net)
except:
return self.makeErrorMessage(0, GetExceptionTracebackStr())
def makeVersionMessage(self, seq):
try:
return NetCommand(CMD_VERSION, seq, VERSION_STRING)
except:
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
def makeThreadKilledMessage(self, id):
try:
return NetCommand(CMD_THREAD_KILL, 0, str(id))
except:
return self.makeErrorMessage(0, GetExceptionTracebackStr())
def makeThreadSuspendMessage(self, thread_id, frame, stop_reason):
""" <xml>
<thread id="id" stop_reason="reason">
<frame id="id" name="functionName " file="file" line="line">
<var variable stuffff....
</frame>
</thread>
"""
try:
cmdTextList = ["<xml>"]
cmdTextList.append('<thread id="%s" stop_reason="%s">' % (thread_id, stop_reason))
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('<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName)))
cmdTextList.append('file="%s" line="%s">"' % (quote(myFile, '/>_= \t'), myLine))
cmdTextList.append(variables)
cmdTextList.append("</frame>")
curFrame = curFrame.f_back
cmdTextList.append("</thread></xml>")
cmdText = ''.join(cmdTextList)
return NetCommand(CMD_THREAD_SUSPEND, 0, cmdText)
except:
return self.makeErrorMessage(0, GetExceptionTracebackStr())
def makeThreadRunMessage(self, id, reason):
try:
return NetCommand(CMD_THREAD_RUN, 0, str(id) + "\t" + str(reason))
except:
return self.makeErrorMessage(0, GetExceptionTracebackStr())
def makeGetVariableMessage(self, seq, payload):
try:
return NetCommand(CMD_GET_VARIABLE, seq, payload)
except Exception:
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
def makeGetFrameMessage(self, seq, payload):
try:
return NetCommand(CMD_GET_FRAME, seq, payload)
except Exception:
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
def makeEvaluateExpressionMessage(self, seq, payload):
try:
return NetCommand(CMD_EVALUATE_EXPRESSION, seq, payload)
except Exception:
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
INTERNAL_TERMINATE_THREAD = 1
INTERNAL_SUSPEND_THREAD = 2
#=======================================================================================================================
# InternalThreadCommand
#=======================================================================================================================
class InternalThreadCommand:
""" internal commands are generated/executed by the debugger.
The reason for their existence is that some commands have to be executed
on specific threads. These are the InternalThreadCommands that get
get posted to PyDB.cmdQueue.
"""
def canBeExecutedBy(self, thread_id):
'''By default, it must be in the same thread to be executed
'''
return self.thread_id == thread_id
def doIt(self, dbg):
raise NotImplementedError("you have to override doIt")
#=======================================================================================================================
# InternalTerminateThread
#=======================================================================================================================
class InternalTerminateThread(InternalThreadCommand):
def __init__(self, thread_id):
self.thread_id = thread_id
def doIt(self, dbg):
PydevdLog(1, "killing ", str(self.thread_id))
cmd = dbg.cmdFactory.makeThreadKilledMessage(self.thread_id)
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalGetVariable
#=======================================================================================================================
class InternalGetVariable(InternalThreadCommand):
""" gets the value of a variable """
def __init__(self, seq, thread_id, frame_id, scope, attrs):
self.sequence = seq
self.thread_id = thread_id
self.frame_id = frame_id
self.scope = scope
self.attributes = attrs
def doIt(self, dbg):
""" Converts request into python variable """
try:
xml = "<xml>"
valDict = pydevd_vars.resolveCompoundVariable(self.thread_id, self.frame_id, self.scope, self.attributes)
keys = valDict.keys()
if hasattr(keys, 'sort'):
keys.sort() #Python 3.0 does not have it
else:
keys = sorted(keys, key=str) #Jython 2.1 does not have it (and all must be compared as strings).
for k in keys:
xml += pydevd_vars.varToXML(valDict[k], str(k))
xml += "</xml>"
cmd = dbg.cmdFactory.makeGetVariableMessage(self.sequence, xml)
dbg.writer.addCommand(cmd)
except Exception:
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving variables " + GetExceptionTracebackStr())
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalChangeVariable
#=======================================================================================================================
class InternalChangeVariable(InternalThreadCommand):
""" changes the value of a variable """
def __init__(self, seq, thread_id, frame_id, scope, attr, expression):
self.sequence = seq
self.thread_id = thread_id
self.frame_id = frame_id
self.scope = scope
self.attr = attr
self.expression = expression
def doIt(self, dbg):
""" Converts request into python variable """
try:
pydevd_vars.changeAttrExpression(self.thread_id, self.frame_id, self.attr, self.expression)
except Exception:
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error changing variable attr:%s expression:%s traceback:%s" % (self.attr, self.expression, GetExceptionTracebackStr()))
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalGetFrame
#=======================================================================================================================
class InternalGetFrame(InternalThreadCommand):
""" gets the value of a variable """
def __init__(self, seq, thread_id, frame_id):
self.sequence = seq
self.thread_id = thread_id
self.frame_id = frame_id
def doIt(self, dbg):
""" Converts request into python variable """
try:
try:
xml = "<xml>"
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
xml += pydevd_vars.frameVarsToXML(frame)
del frame
xml += "</xml>"
cmd = dbg.cmdFactory.makeGetFrameMessage(self.sequence, xml)
dbg.writer.addCommand(cmd)
except pydevd_vars.FrameNotFoundError:
#pydevd_vars.dumpFrames(self.thread_id)
#don't print this error: frame not found: means that the client is not synchronized (but that's ok)
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Frame not found: %s from thread: %s" % (self.frame_id, self.thread_id))
dbg.writer.addCommand(cmd)
except:
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving frame: %s from thread: %s" % (self.frame_id, self.thread_id))
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalEvaluateExpression
#=======================================================================================================================
class InternalEvaluateExpression(InternalThreadCommand):
""" gets the value of a variable """
def __init__(self, seq, thread_id, frame_id, expression, doExec):
self.sequence = seq
self.thread_id = thread_id
self.frame_id = frame_id
self.expression = expression
self.doExec = doExec
def doIt(self, dbg):
""" Converts request into python variable """
try:
result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec)
xml = "<xml>"
xml += pydevd_vars.varToXML(result, "")
xml += "</xml>"
cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml)
dbg.writer.addCommand(cmd)
except:
exc = GetExceptionTracebackStr()
sys.stderr.write('%s\n' % (exc,))
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# PydevdFindThreadById
#=======================================================================================================================
def PydevdFindThreadById(thread_id):
try:
# there was a deadlock here when I did not remove the tracing function when thread was dead
threads = threading.enumerate()
for i in threads:
if thread_id == GetThreadId(i):
return i
sys.stderr.write("Could not find thread %s\n" % thread_id)
sys.stderr.write("Available: %s\n" % [GetThreadId(t) for t in threads])
except:
traceback.print_exc()
return None
+156
View File
@@ -0,0 +1,156 @@
'''
This module holds the constants used for specifying the states of the debugger.
'''
STATE_RUN = 1
STATE_SUSPEND = 2
try:
__setFalse = False
except:
import __builtin__
setattr(__builtin__, 'True', 1)
setattr(__builtin__, 'False', 0)
DEBUG_TRACE_LEVEL = -1
DEBUG_TRACE_BREAKPOINTS = -1
class DebugInfoHolder:
#we have to put it here because it can be set through the command line (so, the
#already imported references would not have it).
DEBUG_RECORD_SOCKET_READS = False
#Optimize with psyco? This gave a 50% speedup in the debugger in tests
USE_PSYCO_OPTIMIZATION = True
#Hold a reference to the original _getframe (because psyco will change that as soon as it's imported)
import sys #Note: the sys import must be here anyways (others depend on it)
GetFrame = sys._getframe
#Used to determine the maximum size of each variable passed to eclipse -- having a big value here may make
#the communication slower -- as the variables are being gathered lazily in the latest version of eclipse,
#this value was raised from 200 to 1000.
MAXIMUM_VARIABLE_REPRESENTATION_SIZE = 1000
import threading
import os
_nextThreadIdLock = threading.Lock()
#=======================================================================================================================
# Python 3?
#=======================================================================================================================
IS_PY3K = False
try:
if sys.version_info[0] >= 3:
IS_PY3K = True
except AttributeError:
pass #Not all versions have sys.version_info
#=======================================================================================================================
# Jython?
#=======================================================================================================================
try:
import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
def DictContains(d, key):
return d.has_key(key)
except:
try:
#Py3k does not have has_key anymore, and older versions don't have __contains__
DictContains = dict.__contains__
except:
DictContains = dict.has_key
#=======================================================================================================================
# NextId
#=======================================================================================================================
class NextId:
def __init__(self):
self._id = 0
def __call__(self):
#No need to synchronize here
self._id += 1
return self._id
_nextThreadId = NextId()
#=======================================================================================================================
# GetThreadId
#=======================================================================================================================
def GetThreadId(thread):
try:
return thread.__pydevd_id__
except AttributeError:
_nextThreadIdLock.acquire()
try:
#We do a new check with the lock in place just to be sure that nothing changed
if not hasattr(thread, '__pydevd_id__'):
try:
pid = os.getpid()
except AttributeError:
try:
#Jython does not have it!
import java.lang.management.ManagementFactory #@UnresolvedImport -- just for jython
pid = java.lang.management.ManagementFactory.getRuntimeMXBean().getName()
pid = pid.replace('@', '_')
except:
#ok, no pid available (will be unable to debug multiple processes)
pid = '000001'
thread.__pydevd_id__ = 'pid%s_seq%s' % (pid, _nextThreadId())
finally:
_nextThreadIdLock.release()
return thread.__pydevd_id__
#===============================================================================
# Null
#===============================================================================
class Null:
"""
Gotten from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
"""
def __init__(self, *args, **kwargs):
return None
def __call__(self, *args, **kwargs):
return self
def __getattr__(self, mname):
return self
def __setattr__(self, name, value):
return self
def __delattr__(self, name):
return self
def __repr__(self):
return "<Null>"
def __str__(self):
return "Null"
def __len__(self):
return 0
def __getitem__(self):
return self
def __setitem__(self, *args, **kwargs):
pass
def write(self, *args, **kwargs):
pass
def __nonzero__(self):
return 0
if __name__ == '__main__':
if Null():
sys.stdout.write('here\n')
+215
View File
@@ -0,0 +1,215 @@
'''
This module provides utilities to get the absolute filenames so that we can be sure that:
- The case of a file will match the actual file in the filesystem (otherwise breakpoints won't be hit).
- Providing means for the user to make path conversions when doing a remote debugging session in
one machine and debugging in another.
To do that, the PATHS_FROM_CLIENT_TO_SERVER constant must be filled with the appropriate paths.
E.g.:
If the server has the structure
/user/projects/my_project/src/package/module1.py
and the client has:
c:\my_project\src\package\module1.py
the PATHS_FROM_CLIENT_TO_SERVER would have to be:
PATHS_FROM_CLIENT_TO_SERVER = [(r'c:\my_project\src', r'/user/projects/my_project/src')]
@note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the result of those translations
@note: the case of the paths is important! Note that this can be tricky to get right when one machine
uses a case-independent filesystem and the other uses a case-dependent filesystem (if the system being
debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_CLIENT_TO_SERVER).
@note: all the paths with breakpoints must be translated (otherwise they won't be found in the server)
@note: to enable remote debugging in the target machine (pydev extensions in the eclipse installation)
import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, port, suspend)
see parameter docs on pydevd.py
@note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
through the PYTHONPATH (and the PATHS_FROM_CLIENT_TO_SERVER only needs to be set on the target
machine for the paths that'll actually have breakpoints).
'''
from pydevd_constants import * #@UnusedWildImport
import os.path
import sys
import traceback
normcase = os.path.normcase
basename = os.path.basename
exists = os.path.exists
join = os.path.join
try:
rPath = os.path.realpath #@UndefinedVariable
except:
# jython does not support os.path.realpath
# realpath is a no-op on systems without islink support
rPath = os.path.abspath
#defined as a list of tuples where the 1st element of the tuple is the path in the client machine
#and the 2nd element is the path in the server machine.
#see module docstring for more details.
PATHS_FROM_CLIENT_TO_SERVER = []
#example:
#PATHS_FROM_CLIENT_TO_SERVER = [
#(normcase(r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy'),
# normcase(r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx'))]
DEBUG_CLIENT_SERVER_TRANSLATION = False
#caches filled as requested during the debug session
NORM_FILENAME_CONTAINER = {}
NORM_FILENAME_AND_BASE_CONTAINER = {}
NORM_FILENAME_TO_SERVER_CONTAINER = {}
NORM_FILENAME_TO_CLIENT_CONTAINER = {}
def _NormFile(filename):
try:
return NORM_FILENAME_CONTAINER[filename]
except KeyError:
r = normcase(rPath(filename))
#cache it for fast access later
NORM_FILENAME_CONTAINER[filename] = r
return r
#Now, let's do a quick test to see if we're working with a version of python that has no problems
#related to the names generated...
try:
try:
code = rPath.func_code
except AttributeError:
code = rPath.__code__
if not exists(_NormFile(code.co_filename)):
sys.stderr.write('-------------------------------------------------------------------------------\n')
sys.stderr.write('pydev debugger: CRITICAL WARNING: This version of python seems to be incorrectly compiled (internal generated filenames are not absolute)\n')
sys.stderr.write('pydev debugger: The debugger may still function, but it will work slower and may miss breakpoints.\n')
sys.stderr.write('pydev debugger: Related bug: http://bugs.python.org/issue1666807\n')
sys.stderr.write('-------------------------------------------------------------------------------\n')
initial_norm_file = _NormFile
def _NormFile(filename): #Let's redefine _NormFile to work with paths that may be incorrect
ret = initial_norm_file(filename)
if not exists(ret):
#We must actually go on and check if we can find it as if it was a relative path for some of the paths in the pythonpath
for path in sys.path:
ret = initial_norm_file(join(path, filename))
if exists(ret):
break
else:
sys.stderr.write('pydev debugger: Unable to find real location for: %s\n' % (filename,))
ret = filename
return ret
except:
#Don't fail if there's something not correct here -- but at least print it to the user so that we can correct that
traceback.print_exc()
if PATHS_FROM_CLIENT_TO_SERVER:
#Work on the client and server slashes.
client_sep = None
server_sep = None
for client_prefix, server_prefix in PATHS_FROM_CLIENT_TO_SERVER:
if client_sep is not None and server_sep is not None:
break
if client_sep is None:
for c in client_prefix:
if c in ('/', '\\'):
client_sep = c
break
if server_sep is None:
for c in server_prefix:
if c in ('/', '\\'):
server_sep = c
break
#If they're the same or one of them cannot be determined, just make it all None.
if client_sep == server_sep or client_sep is None or server_sep is None:
client_sep = server_sep = None
#only setup translation functions if absolutely needed!
def NormFileToServer(filename):
try:
return NORM_FILENAME_TO_SERVER_CONTAINER[filename]
except KeyError:
#used to translate a path from the client to the debug server
translated = normcase(filename)
for client_prefix, server_prefix in PATHS_FROM_CLIENT_TO_SERVER:
if translated.startswith(client_prefix):
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: replacing to server: %s\n' % (translated,))
translated = translated.replace(client_prefix, server_prefix)
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: sent to server: %s\n' % (translated,))
break
else:
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: to server: unable to find matching prefix for: %s in %s\n' % \
(translated, [x[0] for x in PATHS_FROM_CLIENT_TO_SERVER]))
#Note that when going to the server, we do the replace first and only later do the norm file.
if client_sep is not None:
translated = translated.replace(server_sep, client_sep)
ret = _NormFile(translated)
NORM_FILENAME_TO_SERVER_CONTAINER[filename] = translated
return ret
def NormFileToClient(filename):
try:
return NORM_FILENAME_TO_CLIENT_CONTAINER[filename]
except KeyError:
#used to translate a path from the debug server to the client
translated = normcase(filename)
for client_prefix, server_prefix in PATHS_FROM_CLIENT_TO_SERVER:
if translated.startswith(server_prefix):
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: replacing to client: %s\n' % (translated,))
translated = translated.replace(server_prefix, client_prefix)
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: sent to client: %s\n' % (translated,))
break
else:
if DEBUG_CLIENT_SERVER_TRANSLATION:
sys.stderr.write('pydev debugger: to client: unable to find matching prefix for: %s in %s\n' % \
(translated, [x[1] for x in PATHS_FROM_CLIENT_TO_SERVER]))
#When going to the client, first we do the norm file and only later the replace for slashes.
ret = _NormFile(translated)
if client_sep is not None:
ret = ret.replace(client_sep, server_sep)
NORM_FILENAME_TO_CLIENT_CONTAINER[filename] = ret
return ret
else:
#no translation step needed (just inline the calls)
NormFileToClient = _NormFile
NormFileToServer = _NormFile
def GetFilenameAndBase(frame):
#This one is just internal (so, does not need any kind of client-server translation)
f = frame.f_code.co_filename
try:
return NORM_FILENAME_AND_BASE_CONTAINER[f]
except KeyError:
filename = _NormFile(f)
base = basename(filename)
NORM_FILENAME_AND_BASE_CONTAINER[f] = filename, base
return filename, base
+194
View File
@@ -0,0 +1,194 @@
from pydevd_comm import * #@UnusedWildImport
from pydevd_constants import * #@UnusedWildImport
import traceback #@Reimport
import os.path
basename = os.path.basename
#=======================================================================================================================
# PyDBFrame
#=======================================================================================================================
class PyDBFrame:
'''This makes the tracing for a given frame, so, the trace_dispatch
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 the 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
mainDebugger, filename, info, thread = self._args
breakpoint = mainDebugger.breakpoints.get(filename)
if info.pydev_state == STATE_RUN:
#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)
else:
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 can_skip:
return None
else:
#checks the breakpoint to see if there is a context match in some function
curr_func_name = frame.f_code.co_name
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
for _b, condition, func_name in breakpoint.values(): #jython does not support itervalues()
#will match either global or some function
if 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
#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
try:
line = frame.f_lineno
#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 event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoint is not None \
and DictContains(breakpoint, 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]
if condition is not None:
try:
val = eval(condition, frame.f_globals, frame.f_locals)
if not val:
return self.trace_dispatch
except:
sys.stderr.write('Error while evaluating expression\n')
traceback.print_exc()
return self.trace_dispatch
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:
traceback.print_exc()
raise
#step handling. We stop when we hit the right frame
try:
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')
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 ('?', '<module>'):
curr_func_name = ''
if curr_func_name == info.pydev_func_name:
line = info.pydev_next_line
if frame.f_lineno == line:
stop = True
else:
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
frame.f_lineno = line
frame.f_trace = None
stop = True
else:
stop = False
if stop:
#event is always == line or return at this point
if event == 'line':
self.setSuspend(thread, info.pydev_step_cmd)
self.doWaitSuspend(thread, frame, event, arg)
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)
self.doWaitSuspend(thread, back, event, arg)
else:
#in jython we may not have a back frame
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
sys.exc_clear() #don't keep the traceback
pass #ok, psyco not available
+31
View File
@@ -0,0 +1,31 @@
class IORedirector:
'''This class works to redirect the write function to many streams
'''
def __init__(self, *args):
self._redirectTo = args
def write(self, s):
for r in self._redirectTo:
try:
r.write(s)
except:
pass
class IOBuf:
'''This class works as a replacement for stdio and stderr.
It is a buffer and when its contents are requested, it will erase what
it has so far so that the next return will not return the same contents again.
'''
def __init__(self):
self.buflist = []
def getvalue(self):
b = self.buflist
self.buflist = [] #clear it
return ''.join(b)
def write(self, s):
self.buflist.append(s)
+36
View File
@@ -0,0 +1,36 @@
'''
Psyco stub: should implement all the external API from psyco.
'''
def proxy(func, *args, **kwargs):
return func
def bind(func, *args, **kwargs):
return func
def unbind(func, *args, **kwargs):
return func
def unproxy(func, *args, **kwargs):
return func
def full(*args, **kwargs):
pass
def log(*args, **kwargs):
pass
def runonly(*args, **kwargs):
pass
def background(*args, **kwargs):
pass
def cannotcompile(*args, **kwargs):
pass
def profile(*args, **kwargs):
pass
def stop(*args, **kwargs):
pass
+200
View File
@@ -0,0 +1,200 @@
"""
Copied from the python xreload (available for change)
Alternative to reload().
This works by executing the module in a scratch namespace, and then
patching classes, methods and functions in place. This avoids the
need to patch instances. New objects are copied into the target
namespace.
Some of the many limitations include:
- Global mutable objects other than classes are simply replaced, not patched
- Code using metaclasses is not handled correctly
- Code creating global singletons is not handled correctly
- Functions and methods using decorators (other than classmethod and
staticmethod) is not handled correctly
- Renamings are not handled correctly
- Dependent modules are not reloaded
- When a dependent module contains 'from foo import bar', and
reloading foo deletes foo.bar, the dependent module continues to use
the old foo.bar object rather than failing
- Frozen modules and modules loaded from zip files aren't handled
correctly
- Classes involving __slots__ are not handled correctly
"""
import imp
import sys
import types
def xreload(mod):
"""Reload a module in place, updating classes, methods and functions.
Args:
mod: a module object
Returns:
The (updated) input object itself.
"""
# Get the module name, e.g. 'foo.bar.whatever'
modname = mod.__name__
# Get the module namespace (dict) early; this is part of the type check
modns = mod.__dict__
# Parse it into package name and module name, e.g. 'foo.bar' and 'whatever'
i = modname.rfind(".")
if i >= 0:
pkgname, modname = modname[:i], modname[i+1:]
else:
pkgname = None
# Compute the search path
if pkgname:
# We're not reloading the package, only the module in it
pkg = sys.modules[pkgname]
path = pkg.__path__ # Search inside the package
else:
# Search the top-level module path
pkg = None
path = None # Make find_module() uses the default search path
# Find the module; may raise ImportError
(stream, filename, (suffix, mode, kind)) = imp.find_module(modname, path)
# Turn it into a code object
try:
# Is it Python source code or byte code read from a file?
if kind not in (imp.PY_COMPILED, imp.PY_SOURCE):
# Fall back to built-in reload()
return reload(mod)
if kind == imp.PY_SOURCE:
source = stream.read()
code = compile(source, filename, "exec")
else:
import marshal
code = marshal.load(stream)
finally:
if stream:
stream.close()
# Execute the code. We copy the module dict to a temporary; then
# clear the module dict; then execute the new code in the module
# dict; then swap things back and around. This trick (due to
# Glyph Lefkowitz) ensures that the (readonly) __globals__
# attribute of methods and functions is set to the correct dict
# object.
tmpns = modns.copy()
modns.clear()
modns["__name__"] = tmpns["__name__"]
exec(code, modns)
# Now we get to the hard part
oldnames = set(tmpns)
newnames = set(modns)
# Update attributes in place
for name in oldnames & newnames:
modns[name] = _update(tmpns[name], modns[name])
# Done!
return mod
def _update(oldobj, newobj):
"""Update oldobj, if possible in place, with newobj.
If oldobj is immutable, this simply returns newobj.
Args:
oldobj: the object to be updated
newobj: the object used as the source for the update
Returns:
either oldobj, updated in place, or newobj.
"""
if oldobj is newobj:
# Probably something imported
return newobj
if type(oldobj) is not type(newobj):
# Cop-out: if the type changed, give up
return newobj
if hasattr(newobj, "__reload_update__"):
# Provide a hook for updating
return newobj.__reload_update__(oldobj)
if hasattr(types, 'ClassType'):
classtype = types.ClassType
else:
classtype = type
if isinstance(newobj, classtype):
return _update_class(oldobj, newobj)
if isinstance(newobj, types.FunctionType):
return _update_function(oldobj, newobj)
if isinstance(newobj, types.MethodType):
return _update_method(oldobj, newobj)
if isinstance(newobj, classmethod):
return _update_classmethod(oldobj, newobj)
if isinstance(newobj, staticmethod):
return _update_staticmethod(oldobj, newobj)
# Not something we recognize, just give up
return newobj
# All of the following functions have the same signature as _update()
def _update_function(oldfunc, newfunc):
"""Update a function object."""
oldfunc.__doc__ = newfunc.__doc__
oldfunc.__dict__.update(newfunc.__dict__)
oldfunc.__code__ = newfunc.__code__
oldfunc.__defaults__ = newfunc.__defaults__
return oldfunc
def _update_method(oldmeth, newmeth):
"""Update a method object."""
# XXX What if im_func is not a function?
_update(oldmeth.im_func, newmeth.im_func)
return oldmeth
def _update_class(oldclass, newclass):
"""Update a class object."""
olddict = oldclass.__dict__
newdict = newclass.__dict__
oldnames = set(olddict)
newnames = set(newdict)
for name in newnames - oldnames:
setattr(oldclass, name, newdict[name])
for name in oldnames - newnames:
delattr(oldclass, name)
for name in oldnames & newnames - set(['__dict__', '__doc__']):
setattr(oldclass, name, _update(olddict[name], newdict[name]))
return oldclass
def _update_classmethod(oldcm, newcm):
"""Update a classmethod update."""
# While we can't modify the classmethod object itself (it has no
# mutable attributes), we *can* extract the underlying function
# (by calling __get__(), which returns a method object) and update
# it in-place. We don't have the class available to pass to
# __get__() but any object except None will do.
_update(oldcm.__get__(0), newcm.__get__(0))
return newcm
def _update_staticmethod(oldsm, newsm):
"""Update a staticmethod update."""
# While we can't modify the staticmethod object itself (it has no
# mutable attributes), we *can* extract the underlying function
# (by calling __get__(), which returns it) and update it in-place.
# We don't have the class available to pass to __get__() but any
# object except None will do.
_update(oldsm.__get__(0), newsm.__get__(0))
return newsm
+352
View File
@@ -0,0 +1,352 @@
try:
import StringIO
except:
import io as StringIO
import traceback
try:
__setFalse = False
except:
import __builtin__
setattr(__builtin__, 'True', 1)
setattr(__builtin__, 'False', 0)
MAX_ITEMS_TO_HANDLE = 500
TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
TOO_LARGE_ATTR = 'Unable to handle:'
#=======================================================================================================================
# UnableToResolveVariableException
#=======================================================================================================================
class UnableToResolveVariableException(Exception):
pass
#=======================================================================================================================
# InspectStub
#=======================================================================================================================
class InspectStub:
def isbuiltin(self, _args):
return False
def isroutine(self, object):
return False
try:
import inspect
except:
inspect = InspectStub()
try:
import java.lang #@UnresolvedImport
except:
pass
#types does not include a MethodWrapperType
try:
MethodWrapperType = type([].__str__)
except:
MethodWrapperType = None
#=======================================================================================================================
# AbstractResolver
#=======================================================================================================================
class AbstractResolver:
'''
This class exists only for documentation purposes to explain how to create a resolver.
Some examples on how to resolve things:
- list: getDictionary could return a dict with index->item and use the index to resolve it later
- set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
- arbitrary instance: getDictionary could return dict with attr_name->attr and use getattr to resolve it later
'''
def resolve(self, var, attribute):
'''
In this method, we'll resolve some child item given the string representation of the item in the key
representing the previously asked dictionary.
@param var: this is the actual variable to be resolved.
@param attribute: this is the string representation of a key previously returned in getDictionary.
'''
raise NotImplementedError
def getDictionary(self, var):
'''
@param var: this is the variable that should have its children gotten.
@return: a dictionary where each pair key, value should be shown to the user as children items
in the variables view for the given var.
'''
raise NotImplementedError
#=======================================================================================================================
# DefaultResolver
#=======================================================================================================================
class DefaultResolver:
'''
DefaultResolver is the class that'll actually resolve how to show some variable.
'''
def resolve(self, var, attribute):
return getattr(var, attribute)
def getDictionary(self, var):
if MethodWrapperType:
return self._getPyDictionary(var)
else:
return self._getJyDictionary(var)
def _getJyDictionary(self, obj):
ret = {}
found = java.util.HashMap()
original = obj
if hasattr(obj, '__class__') and obj.__class__ == java.lang.Class:
#get info about superclasses
classes = []
classes.append(obj)
c = obj.getSuperclass()
while c != None:
classes.append(c)
c = c.getSuperclass()
#get info about interfaces
interfs = []
for obj in classes:
interfs.extend(obj.getInterfaces())
classes.extend(interfs)
#now is the time when we actually get info on the declared methods and fields
for obj in classes:
declaredMethods = obj.getDeclaredMethods()
declaredFields = obj.getDeclaredFields()
for i in range(len(declaredMethods)):
name = declaredMethods[i].getName()
ret[name] = declaredMethods[i].toString()
found.put(name, 1)
for i in range(len(declaredFields)):
name = declaredFields[i].getName()
found.put(name, 1)
#if declaredFields[i].isAccessible():
declaredFields[i].setAccessible(True)
#ret[name] = declaredFields[i].get( declaredFields[i] )
try:
ret[name] = declaredFields[i].get(original)
except:
ret[name] = declaredFields[i].toString()
#this simple dir does not always get all the info, that's why we have the part before
#(e.g.: if we do a dir on String, some methods that are from other interfaces such as
#charAt don't appear)
try:
d = dir(original)
for name in d:
if found.get(name) is not 1:
ret[name] = getattr(original, name)
except:
#sometimes we're unable to do a dir
pass
return ret
def _getPyDictionary(self, var):
filterPrivate = False
filterSpecial = True
filterFunction = True
filterBuiltIn = True
names = dir(var)
d = {}
#Be aware that the order in which the filters are applied attempts to
#optimize the operation by removing as many items as possible in the
#first filters, leaving fewer items for later filters
if filterBuiltIn or filterFunction:
for n in names:
if filterSpecial:
if n.startswith('__') and n.endswith('__'):
continue
if filterPrivate:
if n.startswith('_') or n.endswith('__'):
continue
try:
attr = getattr(var, n)
#filter builtins?
if filterBuiltIn:
if inspect.isbuiltin(attr):
continue
#filter functions?
if filterFunction:
if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
continue
except:
#if some error occurs getting it, let's put it to the user.
strIO = StringIO.StringIO()
traceback.print_exc(file=strIO)
attr = strIO.getvalue()
d[ n ] = attr
return d
#=======================================================================================================================
# DictResolver
#=======================================================================================================================
class DictResolver:
def resolve(self, dict, key):
if key == '__len__':
return None
if '(' not in key:
#we have to treat that because the dict resolver is also used to directly resolve the global and local
#scopes (which already have the items directly)
return dict[key]
#ok, we have to iterate over the items to find the one that matches the id, because that's the only way
#to actually find the reference from the string we have before.
expected_id = int(key.split('(')[-1][:-1])
for key, val in dict.items():
if id(key) == expected_id:
return val
raise UnableToResolveVariableException()
def getDictionary(self, dict):
ret = {}
for key, val in dict.items():
#we need to add the id because otherwise we cannot find the real object to get its contents later on.
key = '%s (%s)' % (key, id(key))
ret[key] = val
ret['__len__'] = len(dict)
return ret
#=======================================================================================================================
# TupleResolver
#=======================================================================================================================
class TupleResolver: #to enumerate tuples and lists
def resolve(self, var, attribute):
'''
@param var: that's the original attribute
@param attribute: that's the key passed in the dict (as a string)
'''
if attribute == '__len__' or attribute == TOO_LARGE_ATTR:
return None
return var[int(attribute)]
def getDictionary(self, var):
#return dict( [ (i, x) for i, x in enumerate(var) ] )
# modified 'cause jython does not have enumerate support
l = len(var)
d = {}
if l < MAX_ITEMS_TO_HANDLE:
format = '%0' + str(int(len(str(l)))) + 'd'
for i, item in zip(range(l), var):
d[ format % i ] = item
else:
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
d['__len__'] = len(var)
return d
#=======================================================================================================================
# SetResolver
#=======================================================================================================================
class SetResolver:
'''
Resolves a set as dict id(object)->object
'''
def resolve(self, var, attribute):
if attribute == '__len__':
return None
attribute = int(attribute)
for v in var:
if id(v) == attribute:
return v
raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var))
def getDictionary(self, var):
d = {}
for item in var:
d[ id(item) ] = item
d['__len__'] = len(var)
return d
#=======================================================================================================================
# InstanceResolver
#=======================================================================================================================
class InstanceResolver:
def resolve(self, var, attribute):
field = var.__class__.getDeclaredField(attribute)
field.setAccessible(True)
return field.get(var)
def getDictionary(self, obj):
ret = {}
declaredFields = obj.__class__.getDeclaredFields()
for i in range(len(declaredFields)):
name = declaredFields[i].getName()
try:
declaredFields[i].setAccessible(True)
ret[name] = declaredFields[i].get(obj)
except:
traceback.print_exc()
return ret
#=======================================================================================================================
# JyArrayResolver
#=======================================================================================================================
class JyArrayResolver:
'''
This resolves a regular Object[] array from java
'''
def resolve(self, var, attribute):
if attribute == '__len__':
return None
return var[int(attribute)]
def getDictionary(self, obj):
ret = {}
for i in range(len(obj)):
ret[ i ] = obj[i]
ret['__len__'] = len(obj)
return ret
defaultResolver = DefaultResolver()
dictResolver = DictResolver()
tupleResolver = TupleResolver()
instanceResolver = InstanceResolver()
jyArrayResolver = JyArrayResolver()
setResolver = SetResolver()
+78
View File
@@ -0,0 +1,78 @@
from pydevd_constants import * #@UnusedWildImport
try:
import cStringIO as StringIO #may not always be available @UnusedImport
except:
try:
import StringIO #@Reimport
except:
import io as StringIO
import threading
import sys #@Reimport
import traceback
class TracingFunctionHolder:
'''This class exists just to keep some variables (so that we don't keep them in the global namespace).
'''
_original_tracing = None
_warn = True
_lock = threading.Lock()
_traceback_limit = 1
_warnings_shown = {}
def GetExceptionTracebackStr():
exc_info = sys.exc_info()
s = StringIO.StringIO()
traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], file=s)
return s.getvalue()
def _GetStackStr(frame):
msg = '\nIf this is needed, please check: ' + \
'\nhttp://pydev.blogspot.com/2007/06/why-cant-pydev-debugger-work-with.html' + \
'\nto see how to restore the debug tracing back correctly.\n'
if TracingFunctionHolder._traceback_limit:
s = StringIO.StringIO()
s.write('Call Location:\n')
traceback.print_stack(f=frame, limit=TracingFunctionHolder._traceback_limit, file=s)
msg = msg + s.getvalue()
return msg
def _InternalSetTrace(tracing_func):
if TracingFunctionHolder._warn:
frame = GetFrame()
if frame is not None and frame.f_back is not None:
if not frame.f_back.f_code.co_filename.lower().endswith('threading.py'):
message = \
'\nPYDEV DEBUGGER WARNING:' + \
'\nsys.settrace() should not be used when the debugger is being used.' + \
'\nThis may cause the debugger to stop working correctly.' + \
'%s' % _GetStackStr(frame.f_back)
if message not in TracingFunctionHolder._warnings_shown:
#only warn about each message once...
TracingFunctionHolder._warnings_shown[message] = 1
sys.stderr.write('%s\n' % (message,))
TracingFunctionHolder._original_tracing(tracing_func)
def SetTrace(tracing_func):
TracingFunctionHolder._lock.acquire()
try:
TracingFunctionHolder._warn = False
_InternalSetTrace(tracing_func)
TracingFunctionHolder._warn = True
finally:
TracingFunctionHolder._lock.release()
def ReplaceSysSetTraceFunc():
if TracingFunctionHolder._original_tracing is None:
TracingFunctionHolder._original_tracing = sys.settrace
sys.settrace = _InternalSetTrace
+389
View File
@@ -0,0 +1,389 @@
""" pydevd_vars deals with variables:
resolution/conversion to XML.
"""
from pydevd_constants import * #@UnusedWildImport
from types import * #@UnusedWildImport
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import sys #@Reimport
try:
from urllib import quote
except:
from urllib.parse import quote #@UnresolvedImport
import threading
import pydevd_resolver
import traceback
#-------------------------------------------------------------------------- defining true and false for earlier versions
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
#------------------------------------------------------------------------------------------------------ resolvers in map
if not sys.platform.startswith("java"):
typeMap = [
#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),
]
try:
typeMap.append((long, None))
except:
pass #not available on all python versions
try:
typeMap.append((unicode, None))
except:
pass #not available on all python versions
try:
typeMap.append((set, pydevd_resolver.setResolver))
except:
pass #not available on all python versions
try:
typeMap.append((frozenset, pydevd_resolver.setResolver))
except:
pass #not available on all python versions
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),
]
if hasattr(core, 'PyJavaInstance'):
#Jython 2.5b3 removed it.
typeMap.append((core.PyJavaInstance, pydevd_resolver.instanceResolver))
def getType(o):
""" returns a triple (typeObject, typeString, resolver
resolver != None means that variable is a container,
and should be displayed as a hierarchy.
Use the resolver to get its attributes.
All container objects should have a resolver.
"""
try:
type_object = type(o)
type_name = type_object.__name__
except:
#This happens for org.python.core.InitModule
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)
if type_name == 'org.python.core.PyArray':
return (type_object, type_name, pydevd_resolver.jyArrayResolver)
for t in typeMap:
if isinstance(o, t[0]):
return (type_object, type_name, t[1])
except:
traceback.print_exc()
#no match return default
return (type_object, type_name, pydevd_resolver.defaultResolver)
def makeValidXmlValue(s):
return s.replace('<', '&lt;').replace('>', '&gt;')
def varToXML(v, name):
""" single variable or dictionary to xml representation """
type, typeName, resolver = getType(v)
try:
if hasattr(v, '__class__'):
try:
cName = str(v.__class__)
if cName.find('.') != -1:
cName = cName.split('.')[-1]
elif cName.find("'") != -1: #does not have '.' (could be something like <type 'int'>)
cName = cName[cName.index("'") + 1:]
if cName.endswith("'>"):
cName = cName[:-2]
except:
cName = str(v.__class__)
value = '%s: %s' % (cName, v)
else:
value = str(v)
except:
try:
value = repr(v)
except:
value = 'Unable to get repr for %s' % v.__class__
xml = '<var name="%s" type="%s"' % (name, typeName)
if value:
#cannot be too big... communication may not handle it.
if len(value) > MAXIMUM_VARIABLE_REPRESENTATION_SIZE:
value = value[0:MAXIMUM_VARIABLE_REPRESENTATION_SIZE]
value += '...'
#fix to work with unicode values
try:
if not IS_PY3K:
if isinstance(value, unicode):
value = value.encode('utf-8')
else:
if isinstance(value, bytes):
value = value.encode('utf-8')
except TypeError: #in java, unicode is a function
pass
xmlValue = ' value="%s"' % (makeValidXmlValue(quote(value, '/>_= \t')))
else:
xmlValue = ''
if resolver is not None:
xmlCont = ' isContainer="True"'
else:
xmlCont = ''
return ''.join((xml, xmlValue, xmlCont, ' />\n'))
if USE_PSYCO_OPTIMIZATION:
try:
import psyco
varToXML = psyco.proxy(varToXML)
except ImportError:
if hasattr(sys, 'exc_clear'): #jython does not have it
sys.exc_clear() #don't keep the traceback -- clients don't want to see it
def frameVarsToXML(frame):
""" dumps frame variables to XML
<var name="var_name" scope="local" type="type" value="value"/>
"""
xml = ""
keys = frame.f_locals.keys()
if hasattr(keys, 'sort'):
keys.sort() #Python 3.0 does not have it
else:
keys = sorted(keys) #Jython 2.1 does not have it
for k in keys:
try:
v = frame.f_locals[k]
xml += varToXML(v, str(k))
except Exception:
traceback.print_exc()
sys.stderr.write("Unexpected error, recovered safely.\n")
return xml
def iterFrames(initialFrame):
'''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
#cannot use yield
frames = []
while initialFrame is not None:
frames.append(initialFrame)
initialFrame = initialFrame.f_back
return frames
def dumpFrames(thread_id):
sys.stdout.write('dumping frames\n')
if thread_id != GetThreadId(threading.currentThread()) :
raise VariableError("findFrame: must execute on same thread")
curFrame = GetFrame()
for frame in iterFrames(curFrame):
sys.stdout.write('%s\n' % id(frame))
def findFrame(thread_id, frame_id):
""" returns a frame on the thread that has a given frame_id """
if thread_id != GetThreadId(threading.currentThread()) :
raise VariableError("findFrame: must execute on same thread")
curFrame = GetFrame()
if frame_id == "*":
return curFrame # any frame is specified with "*"
frameFound = None
lookingFor = int(frame_id)
for frame in iterFrames(curFrame):
if lookingFor == id(frame):
frameFound = frame
del frame
break
del frame
#for some reason unknown to me, python was holding a reference to the frame
#if we didn't explicitly add those deletes (even after ending this context)
#so, those dels are here for a reason (but still doesn't seem to fix everything)
#Reason: sys.exc_info holding reference to frame that raises exception (so, other places
#need to call sys.exc_clear())
del curFrame
if frameFound is None:
msgFrames = ''
i = 0
for frame in iterFrames(GetFrame()):
i += 1
msgFrames += str(id(frame))
if i % 5 == 0:
msgFrames += '\n'
else:
msgFrames += ' - '
errMsg = '''findFrame: frame not found.
Looking for thread_id:%s, frame_id:%s
Current thread_id:%s, available frames:
%s
''' % (thread_id, lookingFor, GetThreadId(threading.currentThread()), msgFrames)
raise FrameNotFoundError(errMsg)
return frameFound
def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
""" returns the value of the compound variable as a dictionary"""
frame = findFrame(thread_id, frame_id)
attrList = attrs.split('\t')
if scope == "GLOBAL":
var = frame.f_globals
del attrList[0] # globals are special, and they get a single dummy unused attribute
else:
var = frame.f_locals
for k in attrList:
type, _typeName, resolver = getType(var)
var = resolver.resolve(var, k)
try:
type, _typeName, resolver = getType(var)
return resolver.getDictionary(var)
except:
traceback.print_exc()
def evaluateExpression(thread_id, frame_id, expression, doExec):
'''returns the result of the evaluated expression
@param doExec: determines if we should do an exec or an eval
'''
frame = findFrame(thread_id, frame_id)
expression = expression.replace('@LINE@', '\n')
#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
updated_globals = dict()
updated_globals.update(frame.f_globals)
updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals
try:
if doExec:
try:
#try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
#it will have whatever the user actually did)
compiled = compile(expression, '<string>', 'eval')
except:
exec(expression, updated_globals, frame.f_locals)
else:
result = eval(compiled, updated_globals, frame.f_locals)
sys.stdout.write('%s\n' % (result,))
return
else:
result = None
try:
result = eval(expression, updated_globals, frame.f_locals)
except Exception:
s = StringIO()
traceback.print_exc(file=s)
result = s.getvalue()
return result
finally:
#Should not be kept alive if an exception happens and this frame is kept in the stack.
del updated_globals
del frame
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
deficiency -- and it appears that there is no way of making it currently work --
will probably need some change to the python internals)
'''
frame = findFrame(thread_id, frame_id)
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
if attr[:7] == "Globals":
attr = attr[8:]
if attr in frame.f_globals:
frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
else:
#default way (only works for changing it in the topmost frame)
exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals)
except Exception:
traceback.print_exc()
+41
View File
@@ -0,0 +1,41 @@
import sys
#=======================================================================================================================
# PydevdVmType
#=======================================================================================================================
class PydevdVmType:
PYTHON = 'python'
JYTHON = 'jython'
vm_type = None
#=======================================================================================================================
# SetVmType
#=======================================================================================================================
def SetVmType(vm_type):
PydevdVmType.vm_type = vm_type
#=======================================================================================================================
# GetVmType
#=======================================================================================================================
def GetVmType():
if PydevdVmType.vm_type is None:
SetupType()
return PydevdVmType.vm_type
#=======================================================================================================================
# SetupType
#=======================================================================================================================
def SetupType(str=None):
if str is not None:
PydevdVmType.vm_type = str
return
if sys.platform.startswith("java"):
PydevdVmType.vm_type = PydevdVmType.JYTHON
else:
PydevdVmType.vm_type = PydevdVmType.PYTHON
+530
View File
@@ -0,0 +1,530 @@
import fnmatch
import os.path
import re
import sys
import unittest
try:
__setFalse = False
except:
import __builtin__
setattr(__builtin__, 'True', 1)
setattr(__builtin__, 'False', 0)
#=======================================================================================================================
# Jython?
#=======================================================================================================================
try:
import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
def DictContains(d, key):
return d.has_key(key)
except:
try:
#Py3k does not have has_key anymore, and older versions don't have __contains__
DictContains = dict.__contains__
except:
DictContains = dict.has_key
try:
xrange
except:
#Python 3k does not have it
xrange = range
try:
enumerate
except:
def enumerate(lst):
ret = []
i=0
for element in lst:
ret.append((i, element))
i+=1
return ret
#=======================================================================================================================
# getopt code copied since gnu_getopt is not available on jython 2.1
#=======================================================================================================================
class GetoptError(Exception):
opt = ''
msg = ''
def __init__(self, msg, opt=''):
self.msg = msg
self.opt = opt
Exception.__init__(self, msg, opt)
def __str__(self):
return self.msg
def gnu_getopt(args, shortopts, longopts=[]):
"""getopt(args, options[, long_options]) -> opts, args
This function works like getopt(), except that GNU style scanning
mode is used by default. This means that option and non-option
arguments may be intermixed. The getopt() function stops
processing options as soon as a non-option argument is
encountered.
If the first character of the option string is `+', or if the
environment variable POSIXLY_CORRECT is set, then option
processing stops as soon as a non-option argument is encountered.
"""
opts = []
prog_args = []
if isinstance(longopts, ''.__class__):
longopts = [longopts]
else:
longopts = list(longopts)
# Allow options after non-option arguments?
if shortopts.startswith('+'):
shortopts = shortopts[1:]
all_options_first = True
elif os.environ.get("POSIXLY_CORRECT"):
all_options_first = True
else:
all_options_first = False
while args:
if args[0] == '--':
prog_args += args[1:]
break
if args[0][:2] == '--':
opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
elif args[0][:1] == '-':
opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
else:
if all_options_first:
prog_args += args
break
else:
prog_args.append(args[0])
args = args[1:]
return opts, prog_args
def do_longs(opts, opt, longopts, args):
try:
i = opt.index('=')
except ValueError:
optarg = None
else:
opt, optarg = opt[:i], opt[i + 1:]
has_arg, opt = long_has_args(opt, longopts)
if has_arg:
if optarg is None:
if not args:
raise GetoptError('option --%s requires argument' % opt, opt)
optarg, args = args[0], args[1:]
elif optarg:
raise GetoptError('option --%s must not have an argument' % opt, opt)
opts.append(('--' + opt, optarg or ''))
return opts, args
# Return:
# has_arg?
# full option name
def long_has_args(opt, longopts):
possibilities = [o for o in longopts if o.startswith(opt)]
if not possibilities:
raise GetoptError('option --%s not recognized' % opt, opt)
# Is there an exact match?
if opt in possibilities:
return False, opt
elif opt + '=' in possibilities:
return True, opt
# No exact match, so better be unique.
if len(possibilities) > 1:
# XXX since possibilities contains all valid continuations, might be
# nice to work them into the error msg
raise GetoptError('option --%s not a unique prefix' % opt, opt)
assert len(possibilities) == 1
unique_match = possibilities[0]
has_arg = unique_match.endswith('=')
if has_arg:
unique_match = unique_match[:-1]
return has_arg, unique_match
def do_shorts(opts, optstring, shortopts, args):
while optstring != '':
opt, optstring = optstring[0], optstring[1:]
if short_has_arg(opt, shortopts):
if optstring == '':
if not args:
raise GetoptError('option -%s requires argument' % opt,
opt)
optstring, args = args[0], args[1:]
optarg, optstring = optstring, ''
else:
optarg = ''
opts.append(('-' + opt, optarg))
return opts, args
def short_has_arg(opt, shortopts):
for i in range(len(shortopts)):
if opt == shortopts[i] != ':':
return shortopts.startswith(':', i + 1)
raise GetoptError('option -%s not recognized' % opt, opt)
#=======================================================================================================================
# End getopt code
#=======================================================================================================================
#=======================================================================================================================
# parse_cmdline
#=======================================================================================================================
def parse_cmdline():
""" parses command line and returns test directories, verbosity, test filter and test suites
usage:
runfiles.py -v|--verbosity <level> -f|--filter <regex> -t|--tests <Test.test1,Test2> dirs|files
"""
verbosity = 2
test_filter = None
tests = None
optlist, dirs = gnu_getopt(sys.argv[1:], "v:f:t:", ["verbosity=", "filter=", "tests="])
for opt, value in optlist:
if opt in ("-v", "--verbosity"):
verbosity = value
elif opt in ("-f", "--filter"):
test_filter = value.split(',')
elif opt in ("-t", "--tests"):
tests = value.split(',')
if type([]) != type(dirs):
dirs = [dirs]
ret_dirs = []
for d in dirs:
if '|' in d:
#paths may come from the ide separated by |
ret_dirs.extend(d.split('|'))
else:
ret_dirs.append(d)
return ret_dirs, int(verbosity), test_filter, tests
#=======================================================================================================================
# PydevTestRunner
#=======================================================================================================================
class PydevTestRunner:
""" finds and runs a file or directory of files as a unit test """
__py_extensions = ["*.py", "*.pyw"]
__exclude_files = ["__init__.*"]
def __init__(self, test_dir, test_filter=None, verbosity=2, tests=None):
self.test_dir = test_dir
self.__adjust_path()
self.test_filter = self.__setup_test_filter(test_filter)
self.verbosity = verbosity
self.tests = tests
def __adjust_path(self):
""" add the current file or directory to the python path """
path_to_append = None
for n in xrange(len(self.test_dir)):
dir_name = self.__unixify(self.test_dir[n])
if os.path.isdir(dir_name):
if not dir_name.endswith("/"):
self.test_dir[n] = dir_name + "/"
path_to_append = os.path.normpath(dir_name)
elif os.path.isfile(dir_name):
path_to_append = os.path.dirname(dir_name)
else:
msg = ("unknown type. \n%s\nshould be file or a directory.\n" % (dir_name))
raise RuntimeError(msg)
if path_to_append is not None:
#Add it as the last one (so, first things are resolved against the default dirs and
#if none resolves, then we try a relative import).
sys.path.append(path_to_append)
return
def __setup_test_filter(self, test_filter):
""" turn a filter string into a list of filter regexes """
if test_filter is None or len(test_filter) == 0:
return None
return [re.compile("test%s" % f) for f in test_filter]
def __is_valid_py_file(self, fname):
""" tests that a particular file contains the proper file extension
and is not in the list of files to exclude """
is_valid_fname = 0
for invalid_fname in self.__class__.__exclude_files:
is_valid_fname += int(not fnmatch.fnmatch(fname, invalid_fname))
if_valid_ext = 0
for ext in self.__class__.__py_extensions:
if_valid_ext += int(fnmatch.fnmatch(fname, ext))
return is_valid_fname > 0 and if_valid_ext > 0
def __unixify(self, s):
""" stupid windows. converts the backslash to forwardslash for consistency """
return os.path.normpath(s).replace(os.sep, "/")
def __importify(self, s, dir=False):
""" turns directory separators into dots and removes the ".py*" extension
so the string can be used as import statement """
if not dir:
dirname, fname = os.path.split(s)
if fname.count('.') > 1:
#if there's a file named xxx.xx.py, it is not a valid module, so, let's not load it...
return
imp_stmt_pieces = [dirname.replace("\\", "/").replace("/", "."), os.path.splitext(fname)[0]]
if len(imp_stmt_pieces[0]) == 0:
imp_stmt_pieces = imp_stmt_pieces[1:]
return ".".join(imp_stmt_pieces)
else: #handle dir
return s.replace("\\", "/").replace("/", ".")
def __add_files(self, pyfiles, root, files):
""" if files match, appends them to pyfiles. used by os.path.walk fcn """
for fname in files:
if self.__is_valid_py_file(fname):
name_without_base_dir = self.__unixify(os.path.join(root, fname))
pyfiles.append(name_without_base_dir)
return
def find_import_files(self):
""" return a list of files to import """
pyfiles = []
for base_dir in self.test_dir:
if os.path.isdir(base_dir):
if hasattr(os, 'walk'):
for root, dirs, files in os.walk(base_dir):
self.__add_files(pyfiles, root, files)
else:
# jython2.1 is too old for os.walk!
os.path.walk(base_dir, self.__add_files, pyfiles)
elif os.path.isfile(base_dir):
pyfiles.append(base_dir)
return pyfiles
def __get_module_from_str(self, modname, print_exception):
""" Import the module in the given import path.
* Returns the "final" module, so importing "coilib40.subject.visu"
returns the "visu" module, not the "coilib40" as returned by __import__ """
try:
mod = __import__(modname)
for part in modname.split('.')[1:]:
mod = getattr(mod, part)
return mod
except:
if print_exception:
import traceback;traceback.print_exc()
sys.stderr.write('ERROR: Module: %s could not be imported.\n' % (modname,))
return None
def find_modules_from_files(self, pyfiles):
""" returns a lisst of modules given a list of files """
#let's make sure that the paths we want are in the pythonpath...
imports = [self.__importify(s) for s in pyfiles]
system_paths = []
for s in sys.path:
system_paths.append(self.__importify(s, True))
ret = []
for imp in imports:
if imp is None:
continue #can happen if a file is not a valid module
choices = []
for s in system_paths:
if imp.startswith(s):
add = imp[len(s) + 1:]
if add:
choices.append(add)
#sys.stdout.write(' ' + add + ' ')
if not choices:
sys.stdout.write('PYTHONPATH not found for file: %s\n' % imp)
else:
for i, import_str in enumerate(choices):
mod = self.__get_module_from_str(import_str, print_exception=i == len(choices) - 1)
if mod is not None:
ret.append(mod)
break
return ret
def find_tests_from_modules(self, modules):
""" returns the unittests given a list of modules """
loader = unittest.TestLoader()
ret = []
if self.tests:
accepted_classes = {}
accepted_methods = {}
for t in self.tests:
splitted = t.split('.')
if len(splitted) == 1:
accepted_classes[t] = t
elif len(splitted) == 2:
accepted_methods[t] = t
#===========================================================================================================
# GetTestCaseNames
#===========================================================================================================
class GetTestCaseNames:
"""Yes, we need a class for that (cannot use outer context on jython 2.1)"""
def __init__(self, accepted_classes, accepted_methods):
self.accepted_classes = accepted_classes
self.accepted_methods = accepted_methods
def __call__(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass"""
testFnNames = []
className = testCaseClass.__name__
if DictContains(self.accepted_classes, className):
for attrname in dir(testCaseClass):
#If a class is chosen, we select all the 'test' methods'
if attrname.startswith('test') and hasattr(getattr(testCaseClass, attrname), '__call__'):
testFnNames.append(attrname)
else:
for attrname in dir(testCaseClass):
#If we have the class+method name, we must do a full check and have an exact match.
if DictContains(self.accepted_methods, className + '.' + attrname):
if hasattr(getattr(testCaseClass, attrname), '__call__'):
testFnNames.append(attrname)
#sorted() is not available in jython 2.1
testFnNames.sort()
return testFnNames
loader.getTestCaseNames = GetTestCaseNames(accepted_classes, accepted_methods)
ret.extend([loader.loadTestsFromModule(m) for m in modules])
return ret
def filter_tests(self, test_objs):
""" based on a filter name, only return those tests that have
the test case names that match """
test_suite = []
for test_obj in test_objs:
if isinstance(test_obj, unittest.TestSuite):
if test_obj._tests:
test_obj._tests = self.filter_tests(test_obj._tests)
if test_obj._tests:
test_suite.append(test_obj)
elif isinstance(test_obj, unittest.TestCase):
test_cases = []
for tc in test_objs:
try:
testMethodName = tc._TestCase__testMethodName
except AttributeError:
#changed in python 2.5
testMethodName = tc._testMethodName
if self.__match(self.test_filter, testMethodName) and self.__match_tests(self.tests, tc, testMethodName):
test_cases.append(tc)
return test_cases
return test_suite
def __match_tests(self, tests, test_case, test_method_name):
if not tests:
return 1
for t in tests:
class_and_method = t.split('.')
if len(class_and_method) == 1:
#only class name
if class_and_method[0] == test_case.__class__.__name__:
return 1
elif len(class_and_method) == 2:
if class_and_method[0] == test_case.__class__.__name__ and class_and_method[1] == test_method_name:
return 1
return 0
def __match(self, filter_list, name):
""" returns whether a test name matches the test filter """
if filter_list is None:
return 1
for f in filter_list:
if re.match(f, name):
return 1
return 0
def run_tests(self):
""" runs all tests """
sys.stdout.write("Finding files...\n")
files = self.find_import_files()
sys.stdout.write('%s %s\n' % (self.test_dir, '... done'))
sys.stdout.write("Importing test modules ... ")
modules = self.find_modules_from_files(files)
sys.stdout.write("done.\n")
all_tests = self.find_tests_from_modules(modules)
if self.test_filter or self.tests:
if self.test_filter:
sys.stdout.write('Test Filter: %s' % ([p.pattern for p in self.test_filter],))
if self.tests:
sys.stdout.write('Tests to run: %s' % (self.tests,))
all_tests = self.filter_tests(all_tests)
sys.stdout.write('\n')
runner = unittest.TextTestRunner(stream=sys.stdout, descriptions=1, verbosity=verbosity)
runner.run(unittest.TestSuite(all_tests))
return
#=======================================================================================================================
# main
#=======================================================================================================================
if __name__ == '__main__':
dirs, verbosity, test_filter, tests = parse_cmdline()
PydevTestRunner(dirs, test_filter, verbosity, tests).run_tests()
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python
from unittest import TestResult
class TestListener:
""" Simulate a Java interface by providing an abstract class
All methods need to be implemented by classes extending TestListener
"""
# Test was successful.
def addSuccess(self, test):
raise NotImplementedError, "TestListener.addSuccess()"
# An error occured
def addError(self, test, err):
raise NotImplementedError, "TestListener.addError()"
# A failure occurred.
def addFailure(self, test, err):
raise NotImplementedError, "TestListener.addFailure()"
# A test started.
def startTest(self, test):
raise NotImplementedError, "TestListener.startTest()"
# A test ended.
def endTest(self, test):
raise NotImplementedError, "TestListener.endTest()"
class TestResultWithListeners(TestResult):
def __init__(self):
TestResult.__init__(self)
self.listeners = []
def startTest(self, test):
TestResult.startTest(self, test)
for listener in self.listeners:
listener.startTest(test)
def endTest(self, test):
for listener in self.listeners:
listener.endTest(test)
def addSuccess(self, test):
TestResult.addSuccess(self, test)
for listener in self.listeners:
listener.addSuccess(test)
def addError(self, test, err):
TestResult.addError(self, test, err)
for listener in self.listeners:
listener.addError(test, err)
def addFailure(self, test, err):
TestResult.addFailure(self, test, err)
for listener in self.listeners:
listener.addFailure(test, err)
def addListener(self, listener):
self.listeners.append(listener)
def removeListener(self, listener):
self.listeners.remove(listener)
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/pluginTestSrc">
<sourceFolder url="file://$MODULE_DIR$/pluginTestSrc" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="testFramework-java" />
<orderEntry type="library" name="JUnit3" level="project" />
<orderEntry type="module" module-name="java-impl" />
<orderEntry type="module" module-name="python-tests" />
</component>
</module>
+6 -5
View File
@@ -7,12 +7,13 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="testFramework-java" />
<orderEntry type="module" module-name="python" />
<orderEntry type="module" module-name="codeInsight-tests" />
<orderEntry type="library" name="JUnit3" level="project" />
<orderEntry type="module" module-name="idea" />
<orderEntry type="module" module-name="idea-tests" />
<orderEntry type="module" module-name="testFramework" />
<orderEntry type="module" module-name="python-ide" scope="RUNTIME" />
<orderEntry type="module" module-name="platform-ultimate" scope="RUNTIME" />
<orderEntry type="module" module-name="resources-en" scope="RUNTIME" />
<orderEntry type="module" module-name="xdebugger-impl" scope="RUNTIME" />
<orderEntry type="module" module-name="python-py" scope="RUNTIME" />
</component>
</module>
+1
View File
@@ -32,6 +32,7 @@
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="library" name="OroMatcher" level="project" />
<orderEntry type="module" module-name="xml" />
<orderEntry type="module" module-name="xdebugger-api" />
</component>
<component name="copyright">
<Base>
@@ -0,0 +1,252 @@
<component>
<group id="navigation">
<feature
id="navigation.popup.class"
tip-file="GoToClass.html"
min-usage-count="3"
first-show="1"
successive-show="5"/>
<feature
id="navigation.popup.file"
tip-file="GoToClass.html"
min-usage-count="2"
first-show="3"
successive-show="5"/>
<feature
id="navigation.popup.symbol"
tip-file="GoToSymbol.html"
first-show="5"
successive-show="8"/>
<feature
id="navigation.popup.camelprefix"
tip-file="CamelPrefixesInNavigationPopups.html"
min-usage-count="3"
first-show="4"
successive-show="7">
<dependency id="navigation.popup.class"/>
<dependency id="navigation.popup.file"/>
<dependency id="navigation.popup.symbol"/>
</feature>
<feature
id="navigation.popup.wildcards"
tip-file="WildcardsInNavigationPopups.html"
min-usage-count="2"
first-show="3"
successive-show="5">
<dependency id="navigation.popup.class"/>
<dependency id="navigation.popup.file"/>
<dependency id="navigation.popup.symbol"/>
</feature>
<feature
id="navigation.popup.file.structure"
tip-file="FileStructurePopup.html"
min-usage-count="3"
first-show="2"
successive-show="5"
/>
<feature
id="navigation.recent.files"
tip-file="RecentFiles.html"
min-usage-count="3"
first-show="3"
successive-show="5"
/>
<feature
id="navigation.select.in"
tip-file="SelectIn.html"
min-usage-count="3"
first-show="4"
successive-show="7"
/>
<feature
id="navigation.goto.declaration"
tip-file="GoToDeclaration.html"
min-usage-count="15"
first-show="1"
successive-show="4"
/>
<feature
id="navigation.goto.usages"
tip-file="ShowUsages.html"
min-usage-count="3"
first-show="5"
successive-show="5"/>
</group>
<group id="completion">
<feature
id="editing.completion.basic"
tip-file="CodeCompletion.html"
min-usage-count="15"
first-show="1"
successive-show="3"/>
<feature
id="editing.completion.replace"
tip-file="TabInLookups.html"
min-usage-count="10"
first-show="6"
successive-show="8"
/>
<feature
id="editing.completion.finishByDotEtc"
tip-file="DotEtcInLookups.html"
min-usage-count="7"
first-show="4"
successive-show="8"
/>
<feature
id="editing.completion.camelHumps"
tip-file="CamelHumpsInCodeCompletion.html"
min-usage-count="8"
first-show="5"
successive-show="7"
/>
</group>
<group id="editing">
<feature
id="editing.select.word"
tip-file="CtrlW.html"
min-usage-count="10"
first-show="2"
successive-show="4"/>
</group>
<group id="codeassists">
<feature
id="codeassists.quickjavadoc"
tip-file="QuickJavaDoc.html"
min-usage-count="3"
first-show="3"
successive-show="5"
/>
<feature
id="codeassists.quickdefinition"
tip-file="CtrlShiftI.html"
min-usage-count="9"
first-show="3"
successive-show="5"
/>
<feature
id="codeassists.quickdefinition.lookup"
tip-file="CtrlShiftIForLookup.html"
min-usage-count="1"
first-show="5"
successive-show="7">
<dependency id="codeassists.quickdefinition"/>
</feature>
<feature
id="codeassists.quickjavadoc.lookup"
tip-file="QuickJavaDocInLookups.html"
min-usage-count="1"
first-show="5"
successive-show="7">
<dependency id="codeassists.quickjavadoc"/>
</feature>
<feature
id="codeassists.quickjavadoc.ctrln"
tip-file="QuickJavaDocInLookups.html"
min-usage-count="1"
first-show="5"
successive-show="7">
<dependency id="codeassists.quickjavadoc"/>
</feature>
<feature
id="codeassists.surroundwith.statement"
tip-file="RSurroundWith.html"
first-show="5"
successive-show="5"
min-usage-count="4"
/>
<feature
id="codeassists.surroundwith.expression"
tip-file="RSurroundWith.html"
first-show="6"
successive-show="5"
min-usage-count="3"
/>
<feature
id="codeassists.comment.block"
tip-file="RCommentCode.html"
first-show="6"
successive-show="5"
min-usage-count="3"
/>
<feature
id="codeassists.comment.line"
tip-file="RCommentCode.html"
first-show="6"
successive-show="5"
min-usage-count="3"
/>
</group>
<group id="refactoring">
<feature
id="refactoring.introduceVariable"
tip-file="IntroduceVariable.html"
min-usage-count="5"
first-show="1"
successive-show="3"
/>
<feature
id="refactoring.rename"
tip-file="Rename.html"
min-usage-count="2"
first-show="1"
successive-show="3"
/>
</group>
<group id="ui">
<feature
id="ui.tree.speedsearch"
tip-file="SpeedSearch.html"
first-show="5"
successive-show="4"
min-usage-count="6"
/>
<feature
id="ui.scheme.quickswitch"
tip-file="QuickSwitchScheme.html"
first-show="7"
successive-show="3"
min-usage-count="1"
/>
<feature
id="ui.recentchanges"
tip-file="RecentChanges.html"
first-show="5"
successive-show="5"
min-usage-count="1"
/>
<!-- TODO: Uncomment usage in InsertPathAction when FeatureUsageTracker goes to OpenApi
<feature
id="ui.commandLine.insertPath"
name="Insert path in command line"
tip-file="InsertPathShortcut.html"
first-show="6"
successive-show="5"
min-usage-count="3"
/>
-->
<feature id="navigation.popup.action"
tip-file="GoToAction.html"
first-show="7"
successive-show="3"
min-usage-count="1"/>
<feature id="find.recent.search"
tip-file="RecentSearch.html"
first-show="10"
successive-show="3"
min-usage-count="5"/>
<feature id="find.completion"
tip-file="CodeCompletionInSearch.html"
first-show="10"
successive-show="3"
min-usage-count="5"/>
</group>
</component>
+3 -1
View File
@@ -41,7 +41,9 @@
<!-- Run/Debug -->
<configurationType implementation="com.jetbrains.python.run.PythonConfigurationType"/>
<programRunner implementation="com.jetbrains.python.run.PythonRunner"/>
<programRunner implementation="com.jetbrains.python.debugger.PyDebugRunner"/>
<configurationProducer implementation="com.jetbrains.python.run.PythonRunConfigurationProducer"/>
<xdebugger.breakpointType implementation="com.jetbrains.python.debugger.PyLineBreakpointType"/>
<configurationType implementation="com.jetbrains.python.testing.PythonUnitTestConfigurationType"/>
<testSrcLocator implementation="com.jetbrains.python.testing.PythonUnitTestTestIdUrlProvider"/>
@@ -117,7 +119,7 @@
<completion.contributor language="DjangoTemplate"
implementationClass="com.jetbrains.django.completion.DjangoNameCompletionContributor"/>
<psi.referenceContributor implementation="com.jetbrains.django.ref.ViewMethodReferenceContributor"/>
<psi.referenceContributor implementation="com.jetbrains.django.ref.DjangoReferenceContributor"/>
<inspectionToolProvider implementation="com.jetbrains.django.inspection.DjangoInspectionToolProvider"/>
@@ -1,8 +1,8 @@
package com.jetbrains.python;
import org.jetbrains.annotations.NonNls;
import com.intellij.util.PathUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NonNls;
import java.io.File;
@@ -23,4 +23,8 @@ public class PythonHelpersLocator {
}
return new File(jarPath);
}
public static String getHelperPath(String scriptName) {
return new File(getHelpersRoot(), scriptName).getAbsolutePath();
}
}
@@ -21,7 +21,7 @@ public class PythonLanguage extends Language {
private final IFileElementType ELTYPE_FILE = new IStubFileElementType(this) {
@Override
public int getStubVersion() {
return 2;
return 3;
}
};
private final Set<Class<? extends PyAnnotator>> _annotators = new CopyOnWriteArraySet<Class<? extends PyAnnotator>>();
@@ -0,0 +1,226 @@
package com.jetbrains.python.debugger;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.ui.Messages;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.jetbrains.python.debugger.local.PyLocalPositionConverter;
import com.jetbrains.python.debugger.pydev.*;
import org.jetbrains.annotations.NotNull;
import java.net.ServerSocket;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static javax.swing.SwingUtilities.invokeLater;
/**
* @author yole
*/
// todo: bundle messages
// todo: pydevd supports module reloading - look for a way to use the feature
// todo: smart step into
public class PyDebugProcess extends XDebugProcess {
private final PyPositionConverter myPositionConverter = new PyLocalPositionConverter();
private final RemoteDebugger myDebugger;
private final XBreakpointHandler[] myBreakpointHandlers;
private final PyDebuggerEditorsProvider myEditorsProvider;
private final ProcessHandler myProcessHandler;
private final ExecutionConsole myExecutionConsole;
private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<PySourcePosition, XLineBreakpoint>();
private volatile PyThreadInfo mySuspendedThread = null;
protected PyDebugProcess(@NotNull XDebugSession session,
final ServerSocket serverSocket,
final ExecutionConsole executionConsole,
final ProcessHandler processHandler) {
super(session);
session.setPauseActionSupported(true); // todo: implement and drop
myDebugger = new RemoteDebugger(this, serverSocket, 10);
myBreakpointHandlers = new XBreakpointHandler[]{new PyLineBreakpointHandler(this)};
myEditorsProvider = new PyDebuggerEditorsProvider();
myProcessHandler = processHandler;
myExecutionConsole = executionConsole;
}
public PyPositionConverter getPositionConverter() {
return myPositionConverter;
}
public XBreakpointHandler<?>[] getBreakpointHandlers() {
return myBreakpointHandlers;
}
@Override
public XDebuggerEditorsProvider getEditorsProvider() {
return myEditorsProvider;
}
protected ProcessHandler doGetProcessHandler() {
return myProcessHandler;
}
@NotNull
public ExecutionConsole createConsole() {
return myExecutionConsole;
}
@Override
public void sessionInitialized() {
super.sessionInitialized();
ProgressManager.getInstance().run(new Task.Backgroundable(null, "Connecting to debugger", false) {
public void run(@NotNull final ProgressIndicator indicator) {
indicator.setText("Connecting to debugger...");
try {
myDebugger.waitForConnect();
handshake();
registerBreakpoints();
myDebugger.sendCommand(new RunCommand());
}
catch (final Exception e) {
myProcessHandler.destroyProcess();
invokeLater(new Runnable() {
public void run() {
Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), "Connecting to debugger");
}
});
}
}
});
}
private void handshake() throws PyDebuggerException {
final String remoteVersion = myDebugger.handshake();
((ConsoleView)myExecutionConsole).print("Connected to pydevd (version " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
}
private void registerBreakpoints() {
for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
addBreakpoint(entry.getKey(), entry.getValue());
}
}
public void startStepOver() {
resume(ResumeCommand.Mode.STEP_OVER);
}
public void startStepInto() {
resume(ResumeCommand.Mode.STEP_INTO);
}
public void startStepOut() {
resume(ResumeCommand.Mode.STEP_OUT);
}
public void stop() {
myDebugger.disconnect();
}
public void resume() {
resume(ResumeCommand.Mode.RESUME);
}
private void resume(final ResumeCommand.Mode mode) {
if (myDebugger.isConnected() && mySuspendedThread != null) {
final ResumeCommand command = new ResumeCommand(mySuspendedThread.getId(), mode);
mySuspendedThread = null;
myDebugger.sendCommand(command);
}
}
public void runToPosition(@NotNull XSourcePosition position) {
if (myDebugger.isConnected() && mySuspendedThread != null) {
final PySourcePosition pyPosition = myPositionConverter.convert(position);
myDebugger.sendCommand(new SetBreakpointCommand(pyPosition.getFile(), pyPosition.getLine())); // set temp. breakpoint
resume(ResumeCommand.Mode.RESUME);
}
}
public PyDebugValue evaluate(final String expression) throws PyDebuggerException {
PyStackFrame frame = currentFrame();
return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression);
}
public List<PyDebugValue> loadFrame() throws PyDebuggerException {
PyStackFrame frame = currentFrame();
return myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
}
private PyStackFrame currentFrame() throws PyDebuggerException {
if (!myDebugger.isConnected()) {
throw new PyDebuggerException("Disconnected");
}
final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
if (frame == null) {
throw new PyDebuggerException("Process is running");
}
return frame;
}
public void addBreakpoint(PySourcePosition position, XLineBreakpoint breakpoint) {
myRegisteredBreakpoints.put(position, breakpoint);
if (myDebugger.isConnected()) {
myDebugger.sendCommand(new SetBreakpointCommand(position.getFile(), position.getLine()));
}
}
public void removeBreakpoint(PySourcePosition position) {
myRegisteredBreakpoints.remove(position);
if (myDebugger.isConnected()) {
myDebugger.sendCommand(new RemoveBreakpointCommand(position.getFile(), position.getLine()));
}
}
public Collection<PyThreadInfo> getThreads() {
return myDebugger.getThreads();
}
public void threadSuspended(final PyThreadInfo threadInfo) {
if (mySuspendedThread != null) {
// todo: XDebugSession supports only one suspend context
final ResumeCommand command = new ResumeCommand(threadInfo.getId(), ResumeCommand.Mode.RESUME);
myDebugger.sendCommand(command);
return;
}
mySuspendedThread = threadInfo;
final List<PyStackFrameInfo> frames = threadInfo.getFrames();
if (frames != null) {
final PySuspendContext suspendContext = new PySuspendContext(this, threadInfo);
XLineBreakpoint breakpoint = null;
if (threadInfo.isStopOnBreakpoint()) {
final PySourcePosition position = frames.get(0).getPosition();
breakpoint = myRegisteredBreakpoints.get(position);
if (breakpoint == null) {
myDebugger.sendCommand(new RemoveBreakpointCommand(position.getFile(), position.getLine())); // remove temp. breakpoint
}
}
if (breakpoint != null) {
if (!getSession().breakpointReached(breakpoint, suspendContext)) {
resume();
}
}
else {
getSession().positionReached(suspendContext);
}
}
}
}
@@ -0,0 +1,81 @@
package com.jetbrains.python.debugger;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.GenericProgramRunner;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.run.CommandLinePatcher;
import com.jetbrains.python.run.PythonCommandLineState;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.ServerSocket;
/**
* @author yole
*/
public class PyDebugRunner extends GenericProgramRunner {
@NotNull
public String getRunnerId() {
return "PyDebugRunner";
}
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) && profile instanceof AbstractPythonRunConfiguration;
}
protected RunContentDescriptor doExecute(Project project, Executor executor, RunProfileState state,
RunContentDescriptor contentToReuse,
ExecutionEnvironment env) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
final ServerSocket serverSocket;
try {
//noinspection SocketOpenedButNotSafelyClosed
serverSocket = new ServerSocket(0);
}
catch (IOException e) {
throw new ExecutionException("Failed to find free socket port", e);
}
PythonCommandLineState pyState = (PythonCommandLineState) state;
final ExecutionResult result = pyState.execute(new CommandLinePatcher() {
public void patchCommandLine(GeneralCommandLine commandLine) {
final String[] args = new String[]{
PythonHelpersLocator.getHelperPath("pydev/pydevd.py"),
"--client",
"127.0.0.1",
"--port",
String.valueOf(serverSocket.getLocalPort()),
"--file"
};
for (int i=0; i<args.length; i++) {
commandLine.getParametersList().addAt(i, args[i]);
}
}
});
final XDebugSession session = XDebuggerManager.getInstance(project).
startSession(this, env, contentToReuse, new XDebugProcessStarter() {
@NotNull
public XDebugProcess start(@NotNull final XDebugSession session) {
return new PyDebugProcess(session, serverSocket, result.getExecutionConsole(), result.getProcessHandler());
}
});
return session.getRunContentDescriptor();
}
}
@@ -0,0 +1,60 @@
package com.jetbrains.python.debugger;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValueNode;
import com.intellij.xdebugger.ui.DebuggerIcons;
import org.jetbrains.annotations.NotNull;
// todo: extensive types support
// todo: trim long values
// todo: load long lists by parts
public class PyDebugValue extends XValue {
private final String myName;
private final String myType;
private final String myValue;
public PyDebugValue(final String name, final String type, final String value) {
myName = name;
myType = type;
myValue = value;
}
@Override
public void computePresentation(@NotNull XValueNode node) {
node.setPresentation(myName, DebuggerIcons.VALUE_ICON, myType, getValuePresentation(), false);
}
@Override
public void computeChildren(@NotNull XCompositeNode node) {
// todo: support from pydevd needed (?)
super.computeChildren(node);
}
public String getName() {
return myName;
}
public String getType() {
return myType;
}
public String getValue() {
return myValue;
}
private String getValuePresentation() {
String presentation;
if ("str".equals(myType) || "unicode".equals(myType)) {
presentation = "\"" + myValue + "\"";
}
else {
presentation = myValue;
}
return presentation;
}
}
@@ -0,0 +1,69 @@
package com.jetbrains.python.debugger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyDebuggerEditorsProvider extends XDebuggerEditorsProvider {
@NotNull
@Override
public FileType getFileType() {
return PythonFileType.INSTANCE;
}
@NotNull
@Override
public Document createDocument(@NotNull Project project, @NotNull String text, @Nullable XSourcePosition sourcePosition) {
final PyExpressionCodeFragmentImpl fragment = new PyExpressionCodeFragmentImpl(project, "fragment.py", text, true);
/*
final PsiElement element = getContextElement(project, sourcePosition);
System.out.println("element:" + element);
fragment.setContext(element);
*/
// todo: bind to context
return PsiDocumentManager.getInstance(project).getDocument(fragment);
}
/*
@Nullable
private static PsiElement getContextElement(Project project, XSourcePosition sourcePosition) {
if (sourcePosition != null) {
final Document document = FileDocumentManager.getInstance().getDocument(sourcePosition.getFile());
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (psiFile != null) {
int offset = sourcePosition.getOffset();
final int lineEndOffset = document.getLineEndOffset(document.getLineNumber(offset));
do {
PsiElement element = psiFile.findElementAt(offset);
if (element != null && !(element instanceof PsiWhiteSpace || element instanceof PsiComment)) {
PsiElement e = PsiTreeUtil.getParentOfType(element, PyElement.class);
while (e != null) {
if (e instanceof PyPrintStatement) {
return e;
}
e = e.getParent();
}
//return RControlFlowBuilder.getControlFlowNodeElement(element);
}
offset = element.getTextRange().getEndOffset() + 1;
}
while (offset < lineEndOffset);
}
}
return null;
}
*/
}
@@ -0,0 +1,58 @@
package com.jetbrains.python.debugger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyDebuggerEvaluator extends XDebuggerEvaluator {
private final PyDebugProcess myDebugProcess;
public PyDebuggerEvaluator(@NotNull final PyDebugProcess debugProcess) {
myDebugProcess = debugProcess;
}
@Override
public void evaluate(@NotNull String expression, XEvaluationCallback callback) {
// todo: parse expression and use either EVAL or EXEC (add parameter to evaluate)
// todo: think on getting results from EXEC
try {
final PyDebugValue value = myDebugProcess.evaluate(expression);
callback.evaluated(value);
}
catch (Exception e) {
callback.errorOccurred("Unable to evaluate \"" + expression + "\": " + e.getMessage());
}
}
@Override
public TextRange getExpressionRangeAtOffset(Project project, Document document, int offset) {
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (psiFile != null) {
PsiElement element = psiFile.findElementAt(offset);
if (!(element instanceof PyExpression)) {
element = PsiTreeUtil.getParentOfType(element, PyExpression.class);
}
if (element != null && isSimpleEnough(element)) {
return element.getTextRange();
}
}
return null;
}
private static boolean isSimpleEnough(@Nullable PsiElement element) {
return element instanceof PyLiteralExpression || element instanceof PyQualifiedExpression ||
element instanceof PyCallExpression || element instanceof PyBinaryExpression ||
element instanceof PyPrefixExpression || element instanceof PySliceExpression;
}
}
@@ -0,0 +1,14 @@
package com.jetbrains.python.debugger;
public class PyDebuggerException extends Exception {
public PyDebuggerException(String message) {
super(message);
}
public PyDebuggerException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,53 @@
package com.jetbrains.python.debugger;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.intellij.xdebugger.frame.XStackFrame;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class PyExecutionStack extends XExecutionStack {
private final PyDebugProcess myDebugProcess;
private final PyThreadInfo myThreadInfo;
public PyExecutionStack(@NotNull final PyDebugProcess debugProcess, @NotNull final PyThreadInfo threadInfo) {
super(threadInfo.getName());
myDebugProcess = debugProcess;
myThreadInfo = threadInfo;
}
@Override
public XStackFrame getTopFrame() {
final List<PyStackFrameInfo> frames = myThreadInfo.getFrames();
return frames != null ? convert(myDebugProcess, frames.get(0)) : null;
}
@Override
public void computeStackFrames(int firstFrameIndex, XStackFrameContainer container) {
if (myThreadInfo.getState() != PyThreadInfo.State.SUSPENDED) {
container.errorOccured("Frames not available in non-suspended state");
return;
}
final List<PyStackFrameInfo> frames = myThreadInfo.getFrames();
if (frames != null && firstFrameIndex <= frames.size()) {
final List<PyStackFrame> xFrames = new LinkedList<PyStackFrame>();
for (int i = firstFrameIndex; i < frames.size(); i++) {
xFrames.add(convert(myDebugProcess, frames.get(i)));
}
container.addStackFrames(xFrames, true);
}
else {
container.addStackFrames(Collections.<XStackFrame>emptyList(), true);
}
}
private static PyStackFrame convert(final PyDebugProcess debugProcess, final PyStackFrameInfo frameInfo) {
return new PyStackFrame(debugProcess, frameInfo);
}
}
@@ -0,0 +1,33 @@
package com.jetbrains.python.debugger;
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;
public class PyLineBreakpointHandler extends XBreakpointHandler<XLineBreakpoint<XBreakpointProperties>> {
private final PyDebugProcess myDebugProcess;
public PyLineBreakpointHandler(@NotNull final PyDebugProcess debugProcess) {
super(PyLineBreakpointType.class);
myDebugProcess = debugProcess;
}
public void registerBreakpoint(@NotNull XLineBreakpoint<XBreakpointProperties> breakpoint) {
final XSourcePosition position = breakpoint.getSourcePosition();
if (position != null) {
myDebugProcess.addBreakpoint(myDebugProcess.getPositionConverter().convert(position), breakpoint);
}
}
public void unregisterBreakpoint(@NotNull XLineBreakpoint<XBreakpointProperties> breakpoint, boolean temporary) {
final XSourcePosition position = breakpoint.getSourcePosition();
if (position != null) {
myDebugProcess.removeBreakpoint(myDebugProcess.getPositionConverter().convert(position));
}
}
}
@@ -0,0 +1,49 @@
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.jetbrains.python.PythonFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyLineBreakpointType extends XLineBreakpointType<XBreakpointProperties> {
public PyLineBreakpointType() {
super("python-line", "Python Line Breakpoint");
}
public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project project) {
final Ref<Boolean> stoppable = Ref.create(false);
if (file.getFileType() == PythonFileType.INSTANCE) {
final Document document = FileDocumentManager.getInstance().getDocument(file);
if (document != null) {
XDebuggerUtil.getInstance().iterateLine(project, document, line, new Processor<PsiElement>() {
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 VirtualFile file, int line) {
return null;
}
}
@@ -0,0 +1,19 @@
package com.jetbrains.python.debugger;
import com.intellij.xdebugger.XSourcePosition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface PyPositionConverter {
@NotNull
PySourcePosition create(@NotNull final String file, final int line);
@NotNull
PySourcePosition convert(@NotNull final XSourcePosition position);
@Nullable
XSourcePosition convert(@NotNull final PySourcePosition position);
}
@@ -0,0 +1,41 @@
package com.jetbrains.python.debugger;
public abstract class PySourcePosition {
private final String file;
private final int line;
protected PySourcePosition(final String file, final int line) {
this.file = file;
this.line = line;
}
public String getFile() {
return file;
}
public int getLine() {
return line;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PySourcePosition)) return false;
PySourcePosition that = (PySourcePosition)o;
if (line != that.line) return false;
if (file != null ? !file.equals(that.file) : that.file != null) return false;
return true;
}
@Override
public int hashCode() {
int result = file != null ? file.hashCode() : 0;
result = 31 * result + line;
return result;
}
}
@@ -0,0 +1,104 @@
package com.jetbrains.python.debugger;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.ui.DebuggerIcons;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class PyStackFrame extends XStackFrame {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyStackFrame");
private final PyDebugProcess myDebugProcess;
private final PyStackFrameInfo myFrameInfo;
private final XSourcePosition myPosition;
public PyStackFrame(@NotNull final PyDebugProcess debugProcess, @NotNull final PyStackFrameInfo frameInfo) {
myDebugProcess = debugProcess;
myFrameInfo = frameInfo;
myPosition = myDebugProcess.getPositionConverter().convert(frameInfo.getPosition());
}
@Override
public XSourcePosition getSourcePosition() {
return myPosition;
}
@Override
public XDebuggerEvaluator getEvaluator() {
return new PyDebuggerEvaluator(myDebugProcess);
}
@Override
public void customizePresentation(SimpleColoredComponent component) {
component.setIcon(DebuggerIcons.STACK_FRAME_ICON);
if (myPosition == null) {
component.append("<frame not available>", SimpleTextAttributes.GRAY_ATTRIBUTES);
return;
}
boolean isExternal = true;
final VirtualFile file = myPosition.getFile();
final Document document = FileDocumentManager.getInstance().getDocument(file);
if (document != null) {
final Project project = myDebugProcess.getSession().getProject();
isExternal = !ProjectRootManager.getInstance(project).getFileIndex().isInContent(file);
}
component.append(myFrameInfo.getName(), gray(SimpleTextAttributes.REGULAR_ATTRIBUTES, isExternal));
component.append(", ", gray(SimpleTextAttributes.REGULAR_ATTRIBUTES, isExternal));
component.append(myPosition.getFile().getName(), gray(SimpleTextAttributes.REGULAR_ATTRIBUTES, isExternal));
component.append(":", gray(SimpleTextAttributes.REGULAR_ATTRIBUTES, isExternal));
component.append(Integer.toString(myPosition.getLine() + 1), gray(SimpleTextAttributes.REGULAR_ATTRIBUTES, isExternal));
}
private static SimpleTextAttributes gray(SimpleTextAttributes attributes, boolean gray) {
if (!gray) {
return attributes;
}
else {
return (attributes.getStyle() & SimpleTextAttributes.STYLE_ITALIC) != 0
? SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES;
}
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
try {
final List<PyDebugValue> values = myDebugProcess.loadFrame();
node.addChildren(values, true);
// todo: globals (?)
}
catch (PyDebuggerException e) {
LOG.error(e);
}
}
});
}
public String getThreadId() {
return myFrameInfo.getThreadId();
}
public String getFrameId() {
return myFrameInfo.getId();
}
}
@@ -0,0 +1,34 @@
package com.jetbrains.python.debugger;
public class PyStackFrameInfo {
private final String myThreadId;
private final String myId;
private final String myName;
private final PySourcePosition myPosition;
public PyStackFrameInfo(final String threadId, final String id, final String name, final PySourcePosition position) {
myThreadId = threadId;
myId = id;
myName = name;
myPosition = position;
}
public String getThreadId() {
return myThreadId;
}
public String getId() {
return myId;
}
public String getName() {
return myName;
}
public PySourcePosition getPosition() {
return myPosition;
}
}
@@ -0,0 +1,41 @@
package com.jetbrains.python.debugger;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.intellij.xdebugger.frame.XSuspendContext;
import org.jetbrains.annotations.NotNull;
// todo: support for multiple stacks (threads)
public class PySuspendContext extends XSuspendContext {
private final XExecutionStack myActiveStack;
public PySuspendContext(@NotNull final PyDebugProcess debugProcess, @NotNull final PyThreadInfo threadInfo) {
myActiveStack = new PyExecutionStack(debugProcess, threadInfo);
}
@Override
public XExecutionStack getActiveExecutionStack() {
return myActiveStack;
}
@Override
public XExecutionStack[] getExecutionStacks() {
return super.getExecutionStacks();
/*
final Collection<PyThreadInfo> threads = myDebugProcess.getThreads();
if (threads.size() < 1) {
return XExecutionStack.EMPTY_ARRAY;
}
else {
XExecutionStack[] stacks = new XExecutionStack[threads.size()];
int i = 0;
for (PyThreadInfo thread : threads) {
stacks[i++] = new PyExecutionStack(myDebugProcess, thread);
}
return stacks;
}
*/
}
}
@@ -0,0 +1,54 @@
package com.jetbrains.python.debugger;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class PyThreadInfo {
public enum State {
RUNNING, SUSPENDED, KILLED
}
private final String myId;
private final String myName;
private List<PyStackFrameInfo> myFrames;
private State myState;
private final boolean myStopOnBreakpoint; // todo: remove
public PyThreadInfo(final String id, final String name, final List<PyStackFrameInfo> frames, final boolean stopOnBreakpoint) {
myId = id;
myName = name;
myFrames = (frames != null && frames.size() > 0 ? Collections.unmodifiableList(frames) : null);
myStopOnBreakpoint = stopOnBreakpoint;
}
public String getId() {
return myId;
}
public String getName() {
return myName;
}
@Nullable
public synchronized List<PyStackFrameInfo> getFrames() {
return myFrames;
}
public synchronized State getState() {
return myState;
}
public synchronized void updateState(final State state, final List<PyStackFrameInfo> frames) {
myState = state;
myFrames = (frames != null && frames.size() > 0 ? Collections.unmodifiableList(frames) : null);
}
public boolean isStopOnBreakpoint() {
return myStopOnBreakpoint;
}
}
@@ -87,10 +87,10 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool {
}
@NotNull
static List<LocalQuickFix> proposeImportFixes(final PyElement node, String ref_text) {
static Collection<LocalQuickFix> proposeImportFixes(final PyElement node, String ref_text) {
PsiFile exisitng_import_file = null; // if there's a matching existing import, this it the file it imports
ImportFromExistingFix fix = null;
List<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>(2);
Collection<LocalQuickFix> fixes = new HashSet<LocalQuickFix>(2);
Set<String> seen_file_names = new HashSet<String>(); // true import names
// maybe the name is importable via some existing 'import foo' statement, and only needs a qualifier.
// walk up collecting all such statements and analyzing
@@ -279,10 +279,10 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool {
}
// look in other imported modules for this whole name
if (! ref_in_import) {
List<LocalQuickFix> import_fixes = proposeImportFixes(node, ref_text);
Collection<LocalQuickFix> import_fixes = proposeImportFixes(node, ref_text);
if (import_fixes.size() > 0) {
actions.addAll(import_fixes);
Object first_action = import_fixes.get(0);
Object first_action = import_fixes.iterator().next();
if (first_action instanceof HintAction) {
hint_action = ((HintAction)first_action);
}
@@ -0,0 +1,5 @@
package com.jetbrains.python.psi;
public interface PyExpressionCodeFragment extends PyFile {
}
@@ -36,5 +36,8 @@ public interface PyFile extends PyElement, PsiFile, PyDocStringOwner {
@return an URL of file, maybe bogus if virtual file is not present.
*/
@NotNull
String getUrl();
String getUrl();
@Nullable
PyFunction findTopLevelFunction(String name);
}
@@ -0,0 +1,69 @@
package com.jetbrains.python.psi.impl;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.SingleRootFileViewProvider;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.file.impl.FileManager;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.testFramework.LightVirtualFile;
import com.jetbrains.python.psi.PyExpressionCodeFragment;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* clone of JSExpressionCodeFragment
*/
public class PyExpressionCodeFragmentImpl extends PyFileImpl implements PyExpressionCodeFragment {
private PsiElement myContext;
private boolean myPhysical;
private FileViewProvider myViewProvider;
public PyExpressionCodeFragmentImpl(Project project, @NonNls String name, CharSequence text, boolean isPhysical) {
super(((PsiManagerEx)PsiManager.getInstance(project)).getFileManager().createFileViewProvider(
new LightVirtualFile(name, FileTypeManager.getInstance().getFileTypeByFileName(name), text), isPhysical)
);
myPhysical = isPhysical;
((SingleRootFileViewProvider)getViewProvider()).forceCachedPsi(this);
}
protected PyExpressionCodeFragmentImpl clone() {
final PyExpressionCodeFragmentImpl clone = (PyExpressionCodeFragmentImpl)cloneImpl((FileElement)calcTreeElement().clone());
clone.myPhysical = false;
clone.myOriginalFile = this;
FileManager fileManager = ((PsiManagerEx)getManager()).getFileManager();
SingleRootFileViewProvider cloneViewProvider = (SingleRootFileViewProvider)fileManager.createFileViewProvider(new LightVirtualFile(getName(), getLanguage(), getText()), false);
cloneViewProvider.forceCachedPsi(clone);
clone.myViewProvider = cloneViewProvider;
return clone;
}
public PsiElement getContext() {
return myContext;
}
@NotNull
public FileViewProvider getViewProvider() {
if(myViewProvider != null) return myViewProvider;
return super.getViewProvider();
}
public boolean isValid() {
if (!super.isValid()) return false;
if (myContext != null && !myContext.isValid()) return false;
return true;
}
public boolean isPhysical() {
return myPhysical;
}
public void setContext(PsiElement context) {
myContext = context;
}
}
@@ -68,6 +68,16 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
return fname;
}
public PyFunction findTopLevelFunction(String name) {
final List<PyFunction> functions = getTopLevelFunctions();
for (PyFunction function : functions) {
if (name.equals(function.getName())) {
return function;
}
}
return null;
}
public Icon getIcon(int flags) {
return PythonFileType.INSTANCE.getIcon();
}
@@ -488,13 +488,13 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
if (from_import_stmt != null) {
final PyReferenceExpression import_src = from_import_stmt.getImportSource();
if (import_src != null) {
processor.setNotice(" | " + import_src.getName());
processor.setNotice(import_src.getName());
PyResolveUtil.treeCrawlUp(processor, true, import_src.resolve()); // names from that module
}
}
}
// include builtin names
processor.setNotice(" | __builtin__");
processor.setNotice("__builtin__");
PyResolveUtil.treeCrawlUp(processor, true, PyBuiltinCache.getInstance(this).getBuiltinsFile()); // names from __builtin__
// if we're a normal module, add module's attrs
@@ -41,7 +41,7 @@ public class VariantsProcessor implements PsiScopeProcessor {
}
protected static LookupElementBuilder setItemNotice(final LookupElementBuilder item, String notice) {
return item.setTailText(notice);
return item.setTypeText(notice);
}
public LookupElement[] getResult() {
@@ -86,7 +86,7 @@ public class VariantsProcessor implements PsiScopeProcessor {
final PyFromImportStatement from_import = (PyFromImportStatement)maybe_from_import;
PyReferenceExpression src = from_import.getImportSource();
if (src != null) {
lookup_item = setItemNotice(lookup_item, " | " + src.getName());
lookup_item = setItemNotice(lookup_item, src.getName());
}
}
}
@@ -7,29 +7,36 @@ import com.jetbrains.python.lexer.PythonLexer;
import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
* User: Alexey.Ivanov
* Date: Aug 19, 2009
* Time: 10:23:26 PM
* @author Alexey.Ivanov
*/
public class PythonNamesValidator implements NamesValidator {
private static final PythonLexer ourLexer = new PythonLexer();
public synchronized boolean isKeyword(@NotNull final String name, final Project project) {
ourLexer.start(name);
if (!PyTokenTypes.KEYWORDS.contains(ourLexer.getTokenType())) {
try {
ourLexer.start(name);
if (!PyTokenTypes.KEYWORDS.contains(ourLexer.getTokenType())) {
return false;
}
ourLexer.advance();
return ourLexer.getTokenType() == null;
}
catch (StringIndexOutOfBoundsException e) {
return false;
}
ourLexer.advance();
return ourLexer.getTokenType() == null;
}
public synchronized boolean isIdentifier(@NotNull final String name, final Project project) {
ourLexer.start(name);
if (ourLexer.getTokenType() != PyTokenTypes.IDENTIFIER) {
try {
ourLexer.start(name);
if (ourLexer.getTokenType() != PyTokenTypes.IDENTIFIER) {
return false;
}
ourLexer.advance();
return ourLexer.getTokenType() == null;
}
catch (StringIndexOutOfBoundsException e) {
return false;
}
ourLexer.advance();
return ourLexer.getTokenType() == null;
}
}
@@ -0,0 +1,10 @@
package com.jetbrains.python.run;
import com.intellij.execution.configurations.GeneralCommandLine;
/**
* @author yole
*/
public interface CommandLinePatcher {
void patchCommandLine(GeneralCommandLine commandLine);
}
@@ -40,7 +40,11 @@ public class PythonCommandLineState extends CommandLineState {
@Override
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
final ProcessHandler processHandler = startProcess();
return execute(null);
}
public ExecutionResult execute(CommandLinePatcher patcher) throws ExecutionException {
final ProcessHandler processHandler = startProcess(patcher);
final ConsoleView console = createAndAttachConsole(getConfig().getProject(), processHandler);
return new DefaultExecutionResult(console, processHandler, createActions(console, processHandler));
@@ -59,7 +63,14 @@ public class PythonCommandLineState extends CommandLineState {
}
protected OSProcessHandler startProcess() throws ExecutionException {
return startProcess(null);
}
protected OSProcessHandler startProcess(CommandLinePatcher patcher) throws ExecutionException {
GeneralCommandLine commandLine = generateCommandLine();
if (patcher != null) {
patcher.patchCommandLine(commandLine);
}
final OSProcessHandler processHandler = new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString());
ProcessTerminatedListener.attach(processHandler);
@@ -27,6 +27,7 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.facet.PythonFacetSettings;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
@@ -34,7 +35,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
@@ -351,57 +354,29 @@ public class PythonSdkType extends SdkType {
return getPythonBinaryPath(sdkHome).getPath();
}
/**
* Copies a number of resources as files to a temporary directory.
* @param resourceNames each file created will have the same name as the resource it's created from.
* @return the temporary directory
* @throws IOException if anything goes wrong.
*/
private static File copyResourcesToTempDir(final String... resourceNames) throws IOException {
final File tempdir = FileUtil.createTempDirectory("pycharm", "");
for (final String resourceName : resourceNames) {
final String text = FileUtil.loadTextAndClose(new InputStreamReader(PythonSdkType.class.getResourceAsStream(resourceName)));
File target = new File(tempdir.getCanonicalPath(), resourceName);
FileWriter out = new FileWriter(target);
out.write(text);
out.close();
}
return tempdir;
}
private final static String GENERATOR3 = "generator3.py";
private final static String FIND_BINARIES = "find_binaries.py";
public static void generateBuiltinStubs(String sdkPath, final String stubsRoot) {
new File(stubsRoot).mkdirs();
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(getInterpreterPath(sdkPath)); // python
commandLine.addParameter(PythonHelpersLocator.getHelperPath(GENERATOR3));
commandLine.addParameter("-d");
commandLine.addParameter(stubsRoot); // -d stubs_root
commandLine.addParameter("-b"); // for builtins
commandLine.addParameter("-u"); // for update-only mode
try {
final File tempDir = copyResourcesToTempDir(GENERATOR3, "pyparsing.py", "pyparsing_py3.py");
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(getInterpreterPath(sdkPath)); // python
commandLine.addParameter(tempDir.getAbsolutePath() + File.separatorChar + GENERATOR3);
commandLine.addParameter("-d"); commandLine.addParameter(stubsRoot); // -d stubs_root
commandLine.addParameter("-b"); // for builtins
commandLine.addParameter("-u"); // for update-only mode
try {
final OSProcessHandler handler = new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString());
handler.startNotify();
handler.waitFor();
handler.destroyProcess();
}
catch (ExecutionException e) {
LOG.error(e);
}
FileUtil.delete(tempDir);
final OSProcessHandler handler = new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString());
handler.startNotify();
handler.waitFor();
handler.destroyProcess();
}
catch (IOException e) {
catch (ExecutionException e) {
LOG.error(e);
}
}
/**
@@ -412,64 +387,50 @@ public class PythonSdkType extends SdkType {
* @param indicator ProgressIndicator to update, or null.
*/
public static void generateBinaryStubs(final String sdkPath, final String stubsRoot, ProgressIndicator indicator) {
if (!new File(stubsRoot).exists()) return;
if (!new File(stubsRoot).exists()) return;
if (indicator != null) {
indicator.setText("Generating skeletons of binary libs");
}
try {
final int RUN_TIMEOUT = 10*1000; // 10 seconds per call is plenty enough; anything more is clearly wrong.
final String bin_path = getInterpreterPath(sdkPath);
final int RUN_TIMEOUT = 10 * 1000; // 10 seconds per call is plenty enough; anything more is clearly wrong.
final String bin_path = getInterpreterPath(sdkPath);
final File tempDir = copyResourcesToTempDir(GENERATOR3, FIND_BINARIES, "pyparsing.py", "pyparsing_py3.py");
final ProcessOutput run_result =
SdkUtil.getProcessOutput(sdkPath, new String[]{bin_path, PythonHelpersLocator.getHelperPath(FIND_BINARIES)});
try {
final ProcessOutput run_result = SdkUtil.getProcessOutput(
sdkPath, new String[] {bin_path, tempDir.getPath() + File.separatorChar + FIND_BINARIES}
);
if (run_result.getExitCode() == 0) {
for (String line : run_result.getStdoutLines()) {
// line = "mod_name path"
int cutpos = line.indexOf(' ');
String modname = line.substring(0, cutpos);
String mod_fname = modname.replace(".", File.separator); // "a.b.c" -> "a/b/c", no ext
String fname = line.substring(cutpos+1);
//String ext = fname.substring(fname.lastIndexOf('.')); // no way ext is absent
// check if it's fresh
File f_orig = new File(fname);
File f_skel = new File(stubsRoot + File.separator + mod_fname + ".py");
if (f_orig.lastModified() >= f_skel.lastModified()) {
// stale skeleton, rebuild
if (indicator != null) {
indicator.setText2(modname);
}
LOG.info("Skeleton for " + modname);
final ProcessOutput gen_result = SdkUtil.getProcessOutput(sdkPath,
new String[] {bin_path, tempDir.getPath() + File.separatorChar + GENERATOR3, "-d", stubsRoot, modname}, RUN_TIMEOUT
);
if (gen_result.getExitCode() != 0) {
StringBuffer sb = new StringBuffer("Skeleton for ");
sb.append(modname).append(" failed. stderr: --");
for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n");
sb.append("--");
LOG.warn(sb.toString());
}
}
if (run_result.getExitCode() == 0) {
for (String line : run_result.getStdoutLines()) {
// line = "mod_name path"
int cutpos = line.indexOf(' ');
String modname = line.substring(0, cutpos);
String mod_fname = modname.replace(".", File.separator); // "a.b.c" -> "a/b/c", no ext
String fname = line.substring(cutpos + 1);
//String ext = fname.substring(fname.lastIndexOf('.')); // no way ext is absent
// check if it's fresh
File f_orig = new File(fname);
File f_skel = new File(stubsRoot + File.separator + mod_fname + ".py");
if (f_orig.lastModified() >= f_skel.lastModified()) {
// stale skeleton, rebuild
if (indicator != null) {
indicator.setText2(modname);
}
LOG.info("Skeleton for " + modname);
final ProcessOutput gen_result = SdkUtil
.getProcessOutput(sdkPath, new String[]{bin_path, PythonHelpersLocator.getHelperPath(GENERATOR3), "-d", stubsRoot, modname},
RUN_TIMEOUT);
if (gen_result.getExitCode() != 0) {
StringBuffer sb = new StringBuffer("Skeleton for ");
sb.append(modname).append(" failed. stderr: --");
for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n");
sb.append("--");
LOG.warn(sb.toString());
}
}
else {
StringBuffer sb = new StringBuffer();
for (String err_line : run_result.getStderrLines()) sb.append(err_line).append("\n");
LOG.error("failed to run " + FIND_BINARIES + ", exit code " + run_result.getExitCode() + ", stderr '" + sb.toString() + "'");
}
}
finally {
FileUtil.delete(tempDir);
}
}
catch (IOException e) {
LOG.error(e);
else {
StringBuffer sb = new StringBuffer();
for (String err_line : run_result.getStderrLines()) sb.append(err_line).append("\n");
LOG.error("failed to run " + FIND_BINARIES + ", exit code " + run_result.getExitCode() + ", stderr '" + sb.toString() + "'");
}
}
@@ -1,4 +1,4 @@
def foo(uno, dos, tres):
def foo(auno, dos, tres):
pass
foo(uno=)
foo(auno=)
+2 -2
View File
@@ -1,4 +1,4 @@
def foo(uno, dos, tres):
def foo(auno, dos, tres):
pass
foo(un<caret>)
foo(aun<caret>)
@@ -1,4 +1,4 @@
def foo(boo1, *boo2, **boo3):
def foo(xboo1, *boo2, **boo3):
pass
foo(boo1=)
foo(xboo1=)
+2 -2
View File
@@ -1,4 +1,4 @@
def foo(boo1, *boo2, **boo3):
def foo(xboo1, *boo2, **boo3):
pass
foo(boo<caret>)
foo(xboo<caret>)
+1 -1
View File
@@ -14,4 +14,4 @@ class <info descr="null" type="INFORMATION" foreground="0x0000ff" background="0x
pass
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">__made_up__</info>(self):
return None
return <info type="INFORMATION">None</info>
@@ -2,4 +2,4 @@
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">foo</info>():
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">a</info>():
yield 1
return False
return <info type="INFORMATION">False</info>
@@ -5,4 +5,4 @@ class A:
class B(A):
def foo(self):
return self.<warning descr="Unresolved attribute reference 'x' for class 'B'">x</warning>
return self.<caret><warning descr="Unresolved attribute reference 'x' for class 'B'">x</warning>
@@ -4,4 +4,4 @@ class A:
a = A()
a.<warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>+1
a.<caret><warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>+1
@@ -3,4 +3,4 @@ class A:
self.x = 1
def foo(self):
a = self.<warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>
a = self.<caret><warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>
+1 -1
View File
@@ -1,4 +1,4 @@
"Doc comment stays intact"
<warning descr="Unresolved reference 'AddImportDoc'">AddImportDoc</warning>.foo
<caret><warning descr="Unresolved reference 'AddImportDoc'">AddImportDoc</warning>.foo
foo = 1
@@ -4,4 +4,4 @@ class A:
a = A()
a.<warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>()
a.<caret><warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>()
@@ -3,4 +3,4 @@ class A:
self.x = 1
def foo(self, a):
self.<warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>(1, a)
self.<caret><warning descr="Unresolved attribute reference 'y' for class 'A'">y</warning>(1, a)
+1 -1
View File
@@ -1,3 +1,3 @@
class A:
def foo<error descr="Method must have a first parameter, usually called 'self'">()</error>: # Add 'self'
def foo<caret><error descr="Method must have a first parameter, usually called 'self'">()</error>: # Add 'self'
pass
@@ -1,3 +1,3 @@
from AddToImportFromFoo import bar
<warning descr="Unresolved reference 'foo'">foo</warning> # must get imported
<caret><warning descr="Unresolved reference 'foo'">foo</warning> # must get imported
@@ -1,3 +1,3 @@
import QualifyByImportFoo
<warning descr="Unresolved reference 'foo'">foo</warning> # must be qualified
<caret><warning descr="Unresolved reference 'foo'">foo</warning> # must be qualified
@@ -1 +1 @@
a = 4; b = 5<warning descr="Trailing semicolon in the statement">;</warning>
a = 4; b = 5<caret><warning descr="Trailing semicolon in the statement">;</warning>
+1 -1
View File
@@ -1,3 +1,3 @@
class A:
def foo(slf): # Rename to 'self'
def foo(<caret>slf): # Rename to 'self'
pass
@@ -2,10 +2,12 @@ package com.jetbrains.python;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.fixtures.LightMarkedTestCase;
import com.jetbrains.python.psi.PyAssignmentStatement;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.PySubscriptionExpression;
import com.jetbrains.python.psi.PyTargetExpression;
import java.util.List;
import java.util.Map;
@@ -14,7 +16,7 @@ import java.util.Map;
* User: dcheryasov
* Date: Dec 11, 2009 2:13:51 AM
*/
public class PyAssignmentMappingTest extends MarkedTestCase {
public class PyAssignmentMappingTest extends LightMarkedTestCase {
public String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/psi/assignment/";
@@ -2,7 +2,7 @@ package com.jetbrains.python;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.testFramework.ResolveTestCase;
import com.jetbrains.python.fixtures.PyResolveTestCase;
import com.jetbrains.python.psi.PyArgumentList;
import com.jetbrains.python.psi.PyDecorator;
import com.jetbrains.python.psi.PyExpression;
@@ -13,7 +13,7 @@ import com.jetbrains.python.psi.PyFunction;
* User: dcheryasov
* Date: Dec 28, 2008 3:50:23 AM
*/
public class PyDecoratorTest extends ResolveTestCase {
public class PyDecoratorTest extends PyResolveTestCase {
private PsiElement find() throws Exception {
PsiReference ref = configureByFile(getTestName(false) + ".py");
return ref.getElement();
@@ -1,185 +1,141 @@
package com.jetbrains.python;
import com.intellij.ide.DataManager;
import com.intellij.idea.Bombed;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.testFramework.LightCodeInsightTestCase;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import java.io.IOException;
import java.util.Calendar;
/**
* @author yole
*/
public class PyIndentTest extends LightCodeInsightTestCase {
private void doTest(String before, String after) throws Exception {
final String name = getTestName(false);
public class PyIndentTest extends PyLightFixtureTestCase {
private void doTest(final String before, String after) throws Exception {
final String name = getTestName(false);
configureFromFileText(name + ".py", before);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
try {
myFixture.configureByText(name + ".py", before);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
});
CommandProcessor.getInstance().executeCommand(myFixture.getProject(), new Runnable() {
public void run() {
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER);
}
}, "", null);
String s = myFixture.getFile().getText();
myFixture.checkResult(after);
}
EditorActionManager actionManager = EditorActionManager.getInstance();
EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_ENTER);
public void testSimpleIndent() throws Exception {
doTest("a=1<caret>", "a=1\n<caret>");
}
actionHandler.execute(getEditor(), DataManager.getInstance().getDataContext());
public void testIndentColon() throws Exception {
doTest("if a:<caret>", "if a:\n <caret>");
}
String s = myFile.getText();
checkResultByText(null, after, false);
}
public void testIndentStatementList() throws Exception {
doTest("if a:<caret>\n print a", "if a:\n <caret>\n print a");
}
public void testSimpleIndent() throws Exception {
doTest("a=1<caret>", "a=1\n<caret>");
}
public void testIndentStatementList2() throws Exception {
doTest("while a:\n print a<caret>", "while a:\n print a\n <caret>");
}
public void testIndentColon() throws Exception {
doTest("if a:<caret>", "if a:\n <caret>");
}
public void testIndentStatementList3() throws Exception {
doTest("if a:\n print a<caret>\n\nprint b", "if a:\n print a\n <caret>\n\nprint b");
}
public void testIndentStatementList() throws Exception {
doTest("if a:<caret>\n print a", "if a:\n <caret>\n print a");
}
public void testIndentOneLineStatementList() throws Exception {
doTest("if a:\n if b: print c<caret>\n print d", "if a:\n if b: print c\n <caret>\n print d");
}
public void testIndentStatementList2() throws Exception {
doTest("while a:\n print a<caret>", "while a:\n print a\n <caret>");
}
public void testIndentOneLineStatementListBreak() throws Exception {
doTest("if a:\n if b:<caret> print c\n print d", "if a:\n if b:\n <caret>print c\n print d");
}
public void testIndentStatementList3() throws Exception {
doTest("if a:\n print a<caret>\n\nprint b",
"if a:\n print a\n <caret>\n\nprint b");
}
@Bombed(month = Calendar.FEBRUARY, day = 17, user = "yole")
public void testAlignInList() throws Exception {
doTest("__all__ = [a,<caret>", "__all__ = [a,\n" + " <caret>");
}
public void testIndentOneLineStatementList() throws Exception {
doTest("if a:\n if b: print c<caret>\n print d",
"if a:\n if b: print c\n <caret>\n print d");
}
public void testAlignInListMiddle() throws Exception {
doTest("__all__ = [a,<caret>\n" + " c]", "__all__ = [a,\n" + " <caret>\n" + " c]");
}
public void testIndentOneLineStatementListBreak() throws Exception {
doTest("if a:\n if b:<caret> print c\n print d",
"if a:\n if b:\n <caret>print c\n print d");
}
public void testAlignInListMiddle2() throws Exception {
doTest("__all__ = [a,\n" + " b,<caret>\n" + " c]",
"__all__ = [a,\n" + " b,\n" + " <caret>\n" + " c]");
}
@Bombed(month = Calendar.FEBRUARY, day = 17, user="yole")
public void testAlignInList() throws Exception {
doTest("__all__ = [a,<caret>",
"__all__ = [a,\n" +
" <caret>");
}
@Bombed(month = Calendar.FEBRUARY, day = 17, user = "yole")
public void testAlignInListComp() throws Exception {
doTest("__all__ = [a for<caret>", "__all__ = [a for\n" + " <caret>");
}
public void testAlignInListMiddle() throws Exception {
doTest("__all__ = [a,<caret>\n" +
" c]",
"__all__ = [a,\n" +
" <caret>\n" +
" c]");
}
public void testClass() throws Exception {
doTest("class A:\n" + " print a<caret>", "class A:\n" + " print a\n" + " <caret>");
}
public void testAlignInListMiddle2() throws Exception {
doTest("__all__ = [a,\n" +
" b,<caret>\n" +
" c]",
"__all__ = [a,\n" +
" b,\n" +
" <caret>\n" +
" c]");
}
@Bombed(month = Calendar.FEBRUARY, day = 17, user="yole")
public void testAlignInListComp() throws Exception {
doTest("__all__ = [a for<caret>",
"__all__ = [a for\n" +
" <caret>");
}
public void testClass2() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" + " if a: print b\n" + " def getAvailableActions(self):<caret>",
"class CombatExpertiseFeat(Ability):\n" + " if a: print b\n" + " def getAvailableActions(self):\n" + " <caret>");
}
public void testClass() throws Exception {
doTest("class A:\n" +
" print a<caret>",
"class A:\n" +
" print a\n" +
" <caret>");
}
public void testClass2_1() throws Exception {
doTest(
"class CombatExpertiseFeat(Ability):\n" + " if a: print b\n" + " def getAvailableActions(self):<caret>\n" + "class C2: pass",
public void testClass2() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" +
" if a: print b\n" +
" def getAvailableActions(self):<caret>",
"class CombatExpertiseFeat(Ability):\n" +
" if a: print b\n" +
" def getAvailableActions(self):\n" +
" <caret>");
}
"class CombatExpertiseFeat(Ability):\n" +
" if a: print b\n" +
" def getAvailableActions(self):\n" +
" <caret>\n" +
"class C2: pass");
}
public void testClass2_1() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" +
" if a: print b\n" +
" def getAvailableActions(self):<caret>\n" +
"class C2: pass",
public void testMultiDedent() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" + " def getAvailableActions(self):\n" + " result = ArrayList()<caret>",
"class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self):\n" +
" result = ArrayList()\n" +
" <caret>");
}
"class CombatExpertiseFeat(Ability):\n" +
" if a: print b\n" +
" def getAvailableActions(self):\n" +
" <caret>\n" +
"class C2: pass");
}
public void testMultiDedent1() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" + " def getAvailableActions(self):\n" + " if a:<caret>",
"class CombatExpertiseFeat(Ability):\n" + " def getAvailableActions(self):\n" + " if a:\n" + " <caret>");
}
public void testMultiDedent() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self):\n" +
" result = ArrayList()<caret>",
"class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self):\n" +
" result = ArrayList()\n" +
" <caret>");
}
public void testMultiDedent2() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" + " def getAvailableActions(self): result = ArrayList()<caret>",
"class CombatExpertiseFeat(Ability):\n" + " def getAvailableActions(self): result = ArrayList()\n" + " <caret>");
}
public void testMultiDedent1() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self):\n" +
" if a:<caret>",
"class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self):\n" +
" if a:\n" +
" <caret>");
}
public void testIfElse() throws Exception {
doTest("if a:<caret>\n" + " b\n" + "else:\n" + " c", "if a:\n" + " <caret>\n" + " b\n" + "else:\n" + " c");
}
public void testMultiDedent2() throws Exception {
doTest("class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self): result = ArrayList()<caret>",
"class CombatExpertiseFeat(Ability):\n" +
" def getAvailableActions(self): result = ArrayList()\n" +
" <caret>");
}
public void testIfElse2() throws Exception {
doTest("if a:\n" + " b\n" + "else:<caret>\n" + " c", "if a:\n" + " b\n" + "else:\n" + " <caret>\n" + " c");
}
public void testIfElse() throws Exception {
doTest("if a:<caret>\n" +
" b\n" +
"else:\n" +
" c",
"if a:\n" +
" <caret>\n" +
" b\n" +
"else:\n" +
" c");
}
public void testIfElse2() throws Exception {
doTest("if a:\n" +
" b\n" +
"else:<caret>\n" +
" c",
"if a:\n" +
" b\n" +
"else:\n" +
" <caret>\n" +
" c");
}
/*
TODO: formatter core problem?
public void testAlignListBeforeEquals() throws Exception {
doTest("__all__ <caret>= [a,\n" +
" b]",
"__all__ \n" +
"<caret>= [a,\n" +
" b]");
}
*/
/*
TODO: formatter core problem?
public void testAlignListBeforeEquals() throws Exception {
doTest("__all__ <caret>= [a,\n" +
" b]",
"__all__ \n" +
"<caret>= [a,\n" +
" b]");
}
*/
}
@@ -1,11 +1,10 @@
package com.jetbrains.python;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.testFramework.PsiTestUtil;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
@@ -15,7 +14,7 @@ import java.util.Collection;
/**
* @author yole
*/
public class PyInheritorsSearchTest extends CodeInsightTestCase {
public class PyInheritorsSearchTest extends PyLightFixtureTestCase {
public void testSimple() throws Exception {
setupProject();
final PyClass pyClass = findClass("A");
@@ -39,16 +38,20 @@ public class PyInheritorsSearchTest extends CodeInsightTestCase {
private void setupProject() throws Exception {
String testName = getTestName(true);
String root = PythonTestUtil.getTestDataPath() + "/inheritors/" + testName;
VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete, false);
PsiTestUtil.addSourceContentToRoots(myModule, rootDir);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
myFixture.copyDirectoryToProject(testName, "");
PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments();
}
private PyClass findClass(final String name) {
final Collection<PyClass> classes = StubIndex.getInstance().get(PyClassNameIndex.KEY, name, myProject,
ProjectScope.getProjectScope(myProject));
final Project project = myFixture.getProject();
final Collection<PyClass> classes = StubIndex.getInstance().get(PyClassNameIndex.KEY, name, project,
ProjectScope.getProjectScope(project));
assertEquals(1, classes.size());
return classes.iterator().next();
}
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/inheritors/";
}
}
@@ -1,16 +1,15 @@
package com.jetbrains.python;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.testFramework.PsiTestUtil;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.psi.*;
/**
* @author yole
*/
public class PyMultiFileResolveTest extends CodeInsightTestCase {
public class PyMultiFileResolveTest extends PyLightFixtureTestCase {
public void testSimple() throws Exception {
PsiElement element = doResolve();
assertTrue(element instanceof PyFile);
@@ -111,17 +110,20 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
private PsiFile prepareFile() throws Exception {
String testName = getTestName(true);
String fileName = getTestName(false) + ".py";
String root = PythonTestUtil.getTestDataPath() + "/resolve/multiFile/" + testName;
VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete, false);
PsiTestUtil.addSourceContentToRoots(myModule, rootDir);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
myFixture.copyDirectoryToProject(testName, "");
PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments();
VirtualFile sourceFile = rootDir.findChild(fileName);
VirtualFile sourceFile = myFixture.findFileInTempDir(fileName);
assert sourceFile != null;
PsiFile psiFile = myPsiManager.findFile(sourceFile);
PsiFile psiFile = myFixture.getPsiManager().findFile(sourceFile);
return psiFile;
}
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/resolve/multiFile/";
}
private PsiElement doResolve() throws Exception {
PsiFile psiFile = prepareFile();
int offset = findMarkerOffset(psiFile);
@@ -137,7 +139,7 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
}
private int findMarkerOffset(final PsiFile psiFile) {
Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiFile);
Document document = PsiDocumentManager.getInstance(myFixture.getProject()).getDocument(psiFile);
assert document != null;
int offset = -1;
for (int i=1; i<document.getLineCount(); i++) {
@@ -4,12 +4,12 @@ import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
import com.intellij.lang.parameterInfo.ParameterInfoUIContextEx;
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.fixtures.LightMarkedTestCase;
import com.jetbrains.python.psi.PyArgumentList;
import com.jetbrains.python.psi.PyUtil;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +25,7 @@ import java.util.Set;
* <br/>User: dcheryasov
* Date: Jul 14, 2009 3:42:44 AM
*/
public class PyParameterInfoTest extends MarkedTestCase {
public class PyParameterInfoTest extends LightMarkedTestCase {
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath()+ "/paramInfo/";
}
@@ -247,7 +247,7 @@ public class PyParameterInfoTest extends MarkedTestCase {
* @throws Exception if it fails
*/
private Collector feignCtrlP(int offset) throws Exception {
Collector collector = new Collector(getProject(), getFile(), offset);
Collector collector = new Collector(myFixture.getProject(), myFixture.getFile(), offset);
PyParameterInfoHandler handler = new PyParameterInfoHandler();
collector.setParameterOwner(handler.findElementForParameterInfo(collector)); // finds arglist, sets items to show
if (collector.getParameterOwner() != null) {
@@ -5,6 +5,7 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.fixtures.LightMarkedTestCase;
import com.jetbrains.python.psi.PyDocStringOwner;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyStringLiteralExpression;
@@ -17,7 +18,7 @@ import java.util.Map;
* User: dcheryasov
* Date: Jun 7, 2009 12:31:07 PM
*/
public class PyQuickDocTest extends MarkedTestCase {
public class PyQuickDocTest extends LightMarkedTestCase {
private PythonDocumentationProvider myProvider;
@Override
@@ -1,30 +1,26 @@
package com.jetbrains.python;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.testFramework.LightProjectDescriptor;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.inspections.PyMethodParametersInspection;
import com.jetbrains.python.inspections.PyTrailingSemicolonInspection;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import gnu.trove.TIntObjectHashMap;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NonNls;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.io.File;
/**
* Test actions that various inspections add.
* User: dcheryasov
* Date: Nov 29, 2008 12:47:08 AM
*/
public class PyQuickFixTest extends DaemonAnalyzerTestCase {
public class PyQuickFixTest extends PyLightFixtureTestCase {
public void testAddImport() throws Exception {
doInspectionTest("AddImport.py", PyUnresolvedReferencesInspection.class, PyBundle.message("ACT.NAME.add.import"), true, true);
@@ -106,20 +102,6 @@ public class PyQuickFixTest extends DaemonAnalyzerTestCase {
);
}
protected VirtualFile[] loadFiles(String[] names) {
VirtualFile[] ret = new VirtualFile[names.length];
String prefix = getTestDataPath();
for (int i=0; i < names.length; i += 1) {
ret[i] = getVirtualFile(prefix+names[i]);
}
return ret;
}
protected void doTest(VirtualFile[] vFile, boolean checkWarnings, boolean checkInfos) throws Exception {
configureByFiles(null, vFile);
doDoTest(checkWarnings, checkInfos);
}
protected @NonNls String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/inspections/";
}
@@ -148,68 +130,32 @@ public class PyQuickFixTest extends DaemonAnalyzerTestCase {
boolean applyFix,
boolean available
) throws Exception {
final LocalInspectionTool tool = (LocalInspectionTool)inspectionClass.newInstance();
enableInspectionTool(tool);
configureByFiles(null, testFiles);
Collection<HighlightInfo> infos = doDoTest(true, false);
doQuickFixTest(infos, quickFixName, applyFix, available, testFiles[0]);
disableInspectionTool(tool.getShortName());
}
protected void doQuickFixTest(Collection<HighlightInfo> infos,
String quickFixName,
boolean applyFix,
boolean shouldBeAvailable,
String s) throws Exception {
final List<IntentionAction> availableActions = new ArrayList<IntentionAction>(1);
TIntObjectHashMap<HighlightInfo> map = new TIntObjectHashMap<HighlightInfo>(infos.size());
for (HighlightInfo info : infos) {
final GutterIconRenderer renderer = info.getGutterIconRenderer();
if (renderer == null) {
assertFalse("There should be one intention for highlight info", map.containsKey(info.startOffset));
map.put(info.startOffset, info);
if (info.quickFixActionRanges != null) {
for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) {
IntentionAction action = pair.first.getAction();
availableActions.add(action);
}
}
}
}
final IntentionAction intentionAction = LightQuickFixTestCase.findActionWithText(availableActions, quickFixName);
if (shouldBeAvailable) {
myFixture.enableInspections(inspectionClass);
myFixture.configureByFiles(testFiles);
myFixture.checkHighlighting(true, false, false);
final IntentionAction intentionAction = myFixture.findSingleIntention(quickFixName);
if (available) {
assertNotNull(intentionAction);
if (applyFix) {
myFixture.launchAction(intentionAction);
myFixture.checkResultByFile(graftBeforeExt(testFiles [0], "_after"));
}
}
else {
assertNull(intentionAction);
}
if (applyFix && shouldBeAvailable) {
intentionAction.invoke(myProject, myEditor, myFile);
checkResultByFile(graftBeforeExt(s, "_after"));
}
}
// Turns "name.ext" to "name_insertion.ext"
private @NonNls String graftBeforeExt(String name, String insertion) {
@NonNls
private static String graftBeforeExt(String name, String insertion) {
int dotpos = name.indexOf('.');
if (dotpos < 0) dotpos = name.length();
return name.substring(0, dotpos) + insertion + name.substring(dotpos, name.length());
}
/*
@Override
protected Sdk getProjectJDK() {
return createMockJdk(PathManager.getHomePath() + "/plugins/python/testData/mockPythonJDK");
}
*/
/*
private static Sdk createMockJdk(String jdkHome) {
File jdkHomeFile = new File(jdkHome);
if (!jdkHomeFile.exists()) return null;
@@ -226,5 +172,18 @@ public class PyQuickFixTest extends DaemonAnalyzerTestCase {
jdk.setVersionString("2.5");
return jdk;
}
*/
}
protected static class PyWithSdkProjectDescriptor extends PyLightProjectDescriptor {
@Override
public Sdk getSdk() {
return createMockJdk(PathManager.getHomePath() + "/plugins/python/testData/mockPythonJDK");
}
}
private static final LightProjectDescriptor ourProjectDescriptor = new PyWithSdkProjectDescriptor();
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return ourProjectDescriptor;
}
}
@@ -2,7 +2,7 @@ package com.jetbrains.python;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.ResolveTestCase;
import com.jetbrains.python.fixtures.PyResolveTestCase;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyFunction;
@@ -13,7 +13,7 @@ import java.util.EnumSet;
* User: dcheryasov
* Date: Aug 21, 2008
*/
public class PyResolveCalleeTest extends ResolveTestCase {
public class PyResolveCalleeTest extends PyResolveTestCase {
private PyCallExpression.PyMarkedFunction resolveCallee() throws Exception {
PsiReference ref = configureByFile(getTestName(false) + ".py");
@@ -9,10 +9,10 @@ import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.ResolveTestCase;
import com.jetbrains.python.fixtures.PyResolveTestCase;
import com.jetbrains.python.psi.*;
public class PyResolveTest extends ResolveTestCase {
public class PyResolveTest extends PyResolveTestCase {
private PsiElement resolve() throws Exception {
PsiReference ref = configureByFile(getTestName(false) + ".py");
return ref.resolve();
@@ -2,7 +2,7 @@ package com.jetbrains.python;
import com.intellij.codeInsight.editorActions.SelectWordHandler;
import com.intellij.ide.DataManager;
import com.intellij.testFramework.LightCodeInsightTestCase;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import org.jetbrains.annotations.NonNls;
import java.io.File;
@@ -10,33 +10,34 @@ import java.io.File;
/**
* @author yole
*/
public class PySelectWordTest extends LightCodeInsightTestCase {
public class PySelectWordTest extends PyLightFixtureTestCase {
public void testWord() throws Exception {
doTest();
}
private void doTest() throws Exception {
myFixture.copyDirectoryToProject("", "");
@NonNls final String path = getTestName(true);
configureByFile(path + "/before.py");
myFixture.configureByFile(path + "/before.py");
int i = 1;
while (true) {
@NonNls String resultPath = path + "/after" + i + ".py";
if (new File(getTestDataPath() + resultPath).exists()) {
performAction();
//System.out.println("comparing with "+resultPath);
checkResultByFile("Step " + i, resultPath, false);
myFixture.checkResultByFile(resultPath, false);
i++;
}
else {
break;
}
}
assertTrue(i>1);
assertTrue(i > 1);
}
private void performAction() {
SelectWordHandler action = new SelectWordHandler(null);
action.execute(getEditor(), DataManager.getInstance().getDataContext());
action.execute(myFixture.getEditor(), DataManager.getInstance().getDataContext());
}
@Override
@@ -1,6 +1,7 @@
package com.jetbrains.python;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.fixtures.LightMarkedTestCase;
import com.jetbrains.python.psi.*;
import java.util.Map;
@@ -10,7 +11,7 @@ import java.util.Map;
* User: dcheryasov
* Date: Mar 15, 2009 3:11:01 AM
*/
public class PyStatementPartsTest extends MarkedTestCase {
public class PyStatementPartsTest extends LightMarkedTestCase {
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/psi/parts/";
@@ -3,49 +3,34 @@
*/
package com.jetbrains.python;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubUpdatingIndex;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyFileImpl;
import com.jetbrains.python.psi.stubs.PyClassStub;
import java.io.IOException;
import java.util.List;
public class PyStubsTest extends CodeInsightTestCase {
private VirtualFile myRootDir;
public class PyStubsTest extends PyLightFixtureTestCase {
private static final String PARSED_ERROR_MSG = "Operations should have been performed on stubs but caused file to be parsed";
protected void setUp() throws Exception {
myRunCommandForTest = false;
super.setUp();
prepareRoots();
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/stubs/";
}
private void assertNotParsed(PyFile file) {
private static void assertNotParsed(PyFile file) {
assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement());
}
private void prepareRoots() throws Exception {
new WriteAction() {
protected void run(final Result result) throws Throwable {
String root = PythonTestUtil.getTestDataPath() + "/stubs/";
myRootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete, false);
PsiTestUtil.addSourceContentToRoots(myModule, myRootDir);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
}
}.execute();
}
public void testStubStructure() throws Exception {
final PyFile file = getTestFile();
final List<PyClass> classes = file.getTopLevelClasses();
@@ -152,7 +137,7 @@ public class PyStubsTest extends CodeInsightTestCase {
assertEquals(1, children.length);
assertSame(pyClass, children[0]);
new WriteCommandAction(myProject, fileImpl) {
new WriteCommandAction(myFixture.getProject(), fileImpl) {
protected void run(final Result result) throws Throwable {
pyClass.setName("RenamedClass");
assertEquals("RenamedClass", pyClass.getName());
@@ -162,7 +147,7 @@ public class PyStubsTest extends CodeInsightTestCase {
StubElement fileStub = fileImpl.getStub();
assertNull("There should be no stub if file holds tree element", fileStub);
FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, getProject(), null);
FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, myFixture.getProject(), null);
fileImpl.unloadContent();
assertNull(fileImpl.getTreeElement()); // Test unload successed.
@@ -177,10 +162,10 @@ public class PyStubsTest extends CodeInsightTestCase {
return getTestFile(getTestName(false) + ".py");
}
private PyFile getTestFile(final String fileName) {
VirtualFile sourceFile = myRootDir.findChild(fileName);
private PyFile getTestFile(final String fileName) throws IOException {
VirtualFile sourceFile = myFixture.copyFileToProject(fileName);
assert sourceFile != null;
PsiFile psiFile = myPsiManager.findFile(sourceFile);
PsiFile psiFile = myFixture.getPsiManager().findFile(sourceFile);
return (PyFile)psiFile;
}
}
@@ -1,49 +0,0 @@
package com.jetbrains.python;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.psi.*;
import com.intellij.testFramework.ResolveTestCase;
/**
* @author yole
*/
public class PyToJavaResolveTest extends ResolveTestCase {
protected Sdk getTestProjectJdk() {
return JavaSdkImpl.getMockJdk15("java 1.5");
}
private PsiElement resolve() throws Exception {
PsiReference ref = configureByFile(getTestName(false) + ".py");
return ref.resolve();
}
public void testSimple() throws Exception {
PsiElement target = resolve();
assertTrue(target instanceof PsiClass);
assertEquals("java.util.ArrayList", ((PsiClass) target).getQualifiedName());
}
public void testMethod() throws Exception {
PsiElement target = resolve();
assertTrue(target instanceof PsiMethod);
assertEquals("java.util.ArrayList", ((PsiMethod) target).getContainingClass().getQualifiedName());
}
public void testField() throws Exception {
PsiElement target = resolve();
assertTrue(target instanceof PsiField);
assertEquals("java.lang.System", ((PsiField) target).getContainingClass().getQualifiedName());
}
public void testReturnValue() throws Exception {
PsiElement target = resolve();
assertTrue(target instanceof PsiMethod);
assertEquals("java.util.List", ((PsiMethod) target).getContainingClass().getQualifiedName());
}
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/resolve/pyToJava/";
}
}
@@ -19,7 +19,6 @@ public class PythonAllTestsSuite {
PyResolveTest.class,
PyMultiFileResolveTest.class,
PyResolveCalleeTest.class,
PyToJavaResolveTest.class,
PyAssignmentMappingTest.class,
PythonCompletionTest.class,
PyInheritorsSearchTest.class,
@@ -30,6 +29,7 @@ public class PythonAllTestsSuite {
PythonDemorganLawIntentionTest.class,
PyQuickFixTest.class,
PySelectWordTest.class,
PySurroundWithTest.class
};
public static TestSuite suite() {
@@ -4,17 +4,15 @@
*/
package com.jetbrains.python;
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.testFramework.LightCodeInsightTestCase;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
public class PythonCompletionTest extends LightCodeInsightTestCase{
public class PythonCompletionTest extends PyLightFixtureTestCase {
private void doTest() throws Exception {
final String testName = getTestName(true);
configureByFile(testName + ".py");
new CodeCompletionHandlerBase(CompletionType.BASIC).invoke(getProject(), getEditor(), getFile());
checkResultByFile(testName + ".after.py");
myFixture.configureByFile(testName + ".py");
myFixture.completeBasic();
myFixture.checkResultByFile(testName + ".after.py");
}
protected String getTestDataPath() {
@@ -4,21 +4,29 @@
*/
package com.jetbrains.python;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.codeInsight.intention.IntentionAction;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
public class PythonDemorganLawIntentionTest extends LightIntentionActionTestCase {
public void test() throws Exception { doAllTests(); }
protected String getBasePath() {
return "/demorgan";
public class PythonDemorganLawIntentionTest extends PyLightFixtureTestCase {
public void test1() throws Exception {
doTest();
}
public void test2() throws Exception {
doTest();
}
public void test3() throws Exception {
doTest();
}
private void doTest() throws Exception {
myFixture.configureByFile("before" + getTestName(false) + ".py");
final IntentionAction action = myFixture.findSingleIntention("DeMorgan Law");
myFixture.launchAction(action);
}
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/intentions";
}
@Override
protected boolean shouldBeAvailableAfterExecution() {
return true;
return PythonTestUtil.getTestDataPath() + "/intentions/demorgan";
}
}
@@ -1,11 +1,11 @@
package com.jetbrains.python;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.EffectType;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import org.jetbrains.annotations.NonNls;
import java.awt.*;
@@ -14,7 +14,7 @@ import java.awt.*;
* Test highlighting added by annotators.
* @author yole
*/
public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
public class PythonHighlightingTest extends PyLightFixtureTestCase {
@NonNls
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/highlighting/";
@@ -63,7 +63,7 @@ public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
}
public void testContinueInFinallyBlock() throws Exception {
doTest();
doTest(false, false);
}
public void testReturnWithArgumentsInGenerator() throws Exception {
@@ -101,11 +101,11 @@ public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
}
private void doTest() throws Exception {
doTest(getTestName(true) + PyNames.DOT_PY, true, true);
myFixture.testHighlighting(true, true, false, getTestName(true) + PyNames.DOT_PY);
}
private void doTest(boolean checkWarnings, boolean checkInfos) throws Exception {
doTest(getTestName(true) + PyNames.DOT_PY, checkWarnings, checkInfos);
myFixture.testHighlighting(checkWarnings, checkInfos, false, getTestName(true) + PyNames.DOT_PY);
}
}
@@ -1,15 +1,17 @@
package com.jetbrains.python;
package com.jetbrains.python.fixtures;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.PsiTestCase;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -19,7 +21,8 @@ import java.util.regex.Pattern;
* User: dcheryasov
* Date: Mar 14, 2009 11:57:52 PM
*/
public abstract class MarkedTestCase extends PsiTestCase {
public abstract class LightMarkedTestCase extends PyLightFixtureTestCase {
protected PsiFile myFile;
/**
* Marker "as expected", any alphanumeric sting in angle brackets.
@@ -67,7 +70,7 @@ public abstract class MarkedTestCase extends PsiTestCase {
* @return mapping of markers to the PSI elements
* @throws Exception
*/
protected Map<String, PsiElement> configureByFileText(String fileText, String fileName, @NonNls String markerRegexp)
protected Map<String, PsiElement> configureByFileText(String fileText, final String fileName, @NonNls String markerRegexp)
throws Exception
{
// build a map of marks to positions, and the text with marks stripped
@@ -75,7 +78,7 @@ public abstract class MarkedTestCase extends PsiTestCase {
Matcher mat = pat.matcher(fileText);
int rest_index = 0; // from here on fileText is not yet looked at
Map<String, Integer> offsets = new HashMap<String, Integer>();
StringBuffer text = new StringBuffer();
final StringBuffer text = new StringBuffer();
while (mat.find(rest_index)) {
String mark = mat.group();
CharSequence prev_part = fileText.subSequence(rest_index, mat.start());
@@ -87,7 +90,17 @@ public abstract class MarkedTestCase extends PsiTestCase {
// create a file and map marks to PSI elements
Map<String, PsiElement> result = new HashMap<String, PsiElement>();
myFile = createFile(myModule, fileName, text.toString());
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
try {
myFile = myFixture.addFileToProject(fileName, text.toString());
myFixture.configureFromExistingVirtualFile(myFile.getVirtualFile());
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
});
for (Map.Entry<String, Integer> entry : offsets.entrySet()) {
result.put(entry.getKey(), myFile.findElementAt(entry.getValue()));
}
@@ -100,4 +113,4 @@ public abstract class MarkedTestCase extends PsiTestCase {
}
protected abstract String getTestDataPath();
}
}
@@ -6,6 +6,8 @@ import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiReference;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
@@ -23,10 +25,12 @@ public abstract class PyLightFixtureTestCase extends UsefulTestCase {
private static final PyLightProjectDescriptor ourPyDescriptor = new PyLightProjectDescriptor();
protected CodeInsightTestFixture myFixture;
private static boolean ourPlatformPrefixInitialized;
@Override
protected void setUp() throws Exception {
super.setUp();
initPlatformPrefix();
IdeaTestFixtureFactory factory = IdeaTestFixtureFactory.getFixtureFactory();
TestFixtureBuilder<IdeaProjectTestFixture> fixtureBuilder = factory.createLightFixtureBuilder(getProjectDescriptor());
final IdeaProjectTestFixture fixture = fixtureBuilder.getFixture();
@@ -53,7 +57,12 @@ public abstract class PyLightFixtureTestCase extends UsefulTestCase {
return ourPyDescriptor;
}
private static class PyLightProjectDescriptor implements LightProjectDescriptor {
protected PsiReference findReferenceBySignature(final String signature) {
int pos = PsiDocumentManager.getInstance(myFixture.getProject()).getDocument(myFixture.getFile()).getText().indexOf(signature);
return myFixture.getFile().findReferenceAt(pos);
}
protected static class PyLightProjectDescriptor implements LightProjectDescriptor {
public ModuleType getModuleType() {
return EmptyModuleType.getInstance();
}
@@ -66,4 +75,19 @@ public abstract class PyLightFixtureTestCase extends UsefulTestCase {
}
}
public static void initPlatformPrefix() {
if (!ourPlatformPrefixInitialized) {
ourPlatformPrefixInitialized = true;
boolean isIDEA = true;
try {
PyLightFixtureTestCase.class.getClassLoader().loadClass("com.intellij.openapi.project.impl.IdeaProjectManagerImpl");
}
catch (ClassNotFoundException e) {
isIDEA = false;
}
if (!isIDEA) {
System.setProperty("idea.platform.prefix", "Python");
}
}
}
}
@@ -0,0 +1,43 @@
package com.jetbrains.python.fixtures;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NonNls;
import java.io.File;
import java.io.IOException;
/**
* @author yole
*/
public abstract class PyResolveTestCase extends PyLightFixtureTestCase {
@NonNls protected static final String MARKER = "<ref>";
protected PsiReference configureByFile(final String filePath) throws Exception {
VirtualFile testDataRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(getTestDataPath()));
final VirtualFile file = testDataRoot.findFileByRelativePath(filePath);
assertNotNull(file);
String fileText = StringUtil.convertLineSeparators(VfsUtil.loadText(file));
int offset = fileText.indexOf(MARKER);
assertTrue(offset >= 0);
fileText = fileText.substring(0, offset) + fileText.substring(offset + MARKER.length());
final String finalFileText = fileText;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
try {
myFixture.configureByText(new File(filePath).getName(), finalFileText);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
});
final PsiReference reference = myFixture.getFile().findReferenceAt(offset);
return reference;
}
}