📄 debuggerservice.py
字号:
#----------------------------------------------------------------------------# Name: DebuggerService.py# Purpose: Debugger Service for Python and PHP## Author: Matt Fryer## Created: 12/9/04# CVS-ID: $Id: DebuggerService.py,v 1.8 2006/04/20 06:26:01 RD Exp $# Copyright: (c) 2004-2005 ActiveGrid, Inc.# License: wxWindows License#----------------------------------------------------------------------------import wximport wx.lib.intctrlimport wx.lib.docviewimport wx.lib.dialogsimport wx.gizmosimport wx._coreimport wx.lib.pydocviewimport Serviceimport STCTextEditorimport CodeEditorimport PythonEditorimport PHPEditorimport PHPDebuggerimport activegrid.model.projectmodel as projectmodelfrom IDE import ACTIVEGRID_BASE_IDEif not ACTIVEGRID_BASE_IDE: import ProcessModelEditorimport wx.lib.scrolledpanel as scrolledimport sysimport timeimport SimpleXMLRPCServerimport xmlrpclibimport osimport threadingimport Queueimport SocketServerimport ProjectEditorimport typesfrom xml.dom.minidom import parse, parseStringimport bz2import pickleimport DebuggerHarnessimport tracebackimport StringIOimport UICommonimport activegrid.util.sysutils as sysutilslibimport subprocessimport shutilif wx.Platform == '__WXMSW__': try: import win32api _PYWIN32_INSTALLED = True except ImportError: _PYWIN32_INSTALLED = False _WINDOWS = Trueelse: _WINDOWS = Falseif not _WINDOWS or _PYWIN32_INSTALLED: import process_ = wx.GetTranslation_VERBOSE = False_WATCHES_ON = Falseimport wx.lib.newevent(UpdateTextEvent, EVT_UPDATE_STDTEXT) = wx.lib.newevent.NewEvent()(UpdateErrorEvent, EVT_UPDATE_ERRTEXT) = wx.lib.newevent.NewEvent()(DebugInternalWebServer, EVT_DEBUG_INTERNAL) = wx.lib.newevent.NewEvent()# Class to read from stdout or stderr and write the result to a text control.# Args: file=file-like object# callback_function= function that takes a single argument, the line of text# read.class OutputReaderThread(threading.Thread): def __init__(self, file, callback_function, callbackOnExit=None, accumulate=True): threading.Thread.__init__(self) self._file = file self._callback_function = callback_function self._keepGoing = True self._lineCount = 0 self._accumulate = accumulate self._callbackOnExit = callbackOnExit self.setDaemon(True) def __del__(self): # See comment on PythonDebuggerUI.StopExecution self._keepGoing = False def run(self): file = self._file start = time.time() output = "" while self._keepGoing: try: # This could block--how to handle that? text = file.readline() if text == '' or text == None: self._keepGoing = False elif not self._accumulate and self._keepGoing: self._callback_function(text) else: # Should use a buffer? StringIO? output += text # Seems as though the read blocks if we got an error, so, to be # sure that at least some of the exception gets printed, always # send the first hundred lines back as they come in. if self._lineCount < 100 and self._keepGoing: self._callback_function(output) self._lineCount += 1 output = "" elif time.time() - start > 0.25 and self._keepGoing: try: self._callback_function(output) except wx._core.PyDeadObjectError: # GUI was killed while we were blocked. self._keepGoing = False start = time.time() output = "" #except TypeError: # pass except: tp, val, tb = sys.exc_info() print "Exception in OutputReaderThread.run():", tp, val self._keepGoing = False if self._callbackOnExit: try: self._callbackOnExit() except wx._core.PyDeadObjectError: pass if _VERBOSE: print "Exiting OutputReaderThread" def AskToStop(self): self._keepGoing = Falseclass Executor: PHP_CGI_BIN_PATH_WIN = "../../3rdparty/php" PHP_CGI_BIN_PATH_UNIX = "../../../3rdparty/php/bin" PHP_CGI_BIN_PATH_OSX = "../3rdparty/php/bin" PHP_CGI_EXEC_WIN = "php-cgi.exe" PHP_CGI_EXEC_UNIX = "php" PHP_CGI_EXEC_OSX = "php-cgi" def GetPythonExecutablePath(): path = UICommon.GetPythonExecPath() if path: return path wx.MessageBox(_("To proceed we need to know the location of the python.exe you would like to use.\nTo set this, go to Tools-->Options and use the 'Python' tab to enter a value.\n"), _("Python Executable Location Unknown")) return None GetPythonExecutablePath = staticmethod(GetPythonExecutablePath) def GetPHPExecutablePath(): if sysutilslib.isWindows(): phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_WIN phpCgiExec = Executor.PHP_CGI_EXEC_WIN elif sys.platform == "darwin": phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_OSX phpCgiExec = Executor.PHP_CGI_EXEC_OSX else: phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_UNIX phpCgiExec = Executor.PHP_CGI_EXEC_UNIX if sysutilslib.isRelease(): phpCgiExecFullPath = os.path.normpath(os.path.join(sysutilslib.mainModuleDir, phpCgiBinPath, phpCgiExec)) else: phpCgiExecFullPath = phpCgiExec if _VERBOSE: print "php cgi executable full path is: %s" % phpCgiExecFullPath return phpCgiExecFullPath GetPHPExecutablePath = staticmethod(GetPHPExecutablePath) def __init__(self, fileName, wxComponent, arg1=None, arg2=None, arg3=None, arg4=None, arg5=None, arg6=None, arg7=None, arg8=None, arg9=None, callbackOnExit=None): self._fileName = fileName self._stdOutCallback = self.OutCall self._stdErrCallback = self.ErrCall self._callbackOnExit = callbackOnExit self._wxComponent = wxComponent if fileName.endswith('.py') or fileName.endswith('.pyc'): self._path = Executor.GetPythonExecutablePath() self._cmd = '"' + self._path + '" -u \"' + fileName + '\"' else: self._path = Executor.GetPHPExecutablePath() self._cmd = '"' + self._path + '"' #Better way to do this? Quotes needed for windows file paths. def spaceAndQuote(text): if text.startswith("\"") and text.endswith("\""): return ' ' + text else: return ' \"' + text + '\"' if(arg1 != None): self._cmd += spaceAndQuote(arg1) if(arg2 != None): self._cmd += spaceAndQuote(arg2) if(arg3 != None): self._cmd += spaceAndQuote(arg3) if(arg4 != None): self._cmd += spaceAndQuote(arg4) if(arg5 != None): self._cmd += spaceAndQuote(arg5) if(arg6 != None): self._cmd += spaceAndQuote(arg6) if(arg7 != None): self._cmd += spaceAndQuote(arg7) if(arg8 != None): self._cmd += spaceAndQuote(arg8) if(arg9 != None): self._cmd += spaceAndQuote(arg9) self._stdOutReader = None self._stdErrReader = None self._process = None def OutCall(self, text): evt = UpdateTextEvent(value = text) wx.PostEvent(self._wxComponent, evt) def ErrCall(self, text): evt = UpdateErrorEvent(value = text) wx.PostEvent(self._wxComponent, evt) def Execute(self, arguments, startIn=None, environment=None): if not startIn: startIn = str(os.getcwd()) startIn = os.path.abspath(startIn) if arguments and arguments != " ": command = self._cmd + ' ' + arguments else: command = self._cmd if _VERBOSE: print "start debugger executable: " + command + "\n" self._process = process.ProcessOpen(command, mode='b', cwd=startIn, env=environment) # Kick off threads to read stdout and stderr and write them # to our text control. self._stdOutReader = OutputReaderThread(self._process.stdout, self._stdOutCallback, callbackOnExit=self._callbackOnExit) self._stdOutReader.start() self._stdErrReader = OutputReaderThread(self._process.stderr, self._stdErrCallback, accumulate=False) self._stdErrReader.start() def DoStopExecution(self): # See comment on PythonDebuggerUI.StopExecution if(self._process != None): self._stdOutReader.AskToStop() self._stdErrReader.AskToStop() try: self._process.kill(gracePeriod=2.0) except: pass self._process = None def GetExecPath(self): return self._pathclass RunCommandUI(wx.Panel): runners = [] def ShutdownAllRunners(): # See comment on PythonDebuggerUI.StopExecution for runner in RunCommandUI.runners: try: runner.StopExecution(None) except wx._core.PyDeadObjectError: pass RunCommandUI.runners = [] ShutdownAllRunners = staticmethod(ShutdownAllRunners) def __init__(self, parent, id, fileName): wx.Panel.__init__(self, parent, id) self._noteBook = parent threading._VERBOSE = _VERBOSE self.KILL_PROCESS_ID = wx.NewId() self.CLOSE_TAB_ID = wx.NewId() # GUI Initialization follows sizer = wx.BoxSizer(wx.HORIZONTAL) self._tb = tb = wx.ToolBar(self, -1, wx.DefaultPosition, (30,1000), wx.TB_VERTICAL| wx.TB_FLAT, "Runner" ) tb.SetToolBitmapSize((16,16)) sizer.Add(tb, 0, wx.EXPAND|wx.ALIGN_LEFT|wx.ALL, 1) close_bmp = getCloseBitmap() tb.AddSimpleTool( self.CLOSE_TAB_ID, close_bmp, _('Close Window')) wx.EVT_TOOL(self, self.CLOSE_TAB_ID, self.OnToolClicked) stop_bmp = getStopBitmap() tb.AddSimpleTool(self.KILL_PROCESS_ID, stop_bmp, _("Stop the Run.")) wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.OnToolClicked) tb.Realize() self._textCtrl = STCTextEditor.TextCtrl(self, wx.NewId()) #id) sizer.Add(self._textCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1) self._textCtrl.SetViewLineNumbers(False) self._textCtrl.SetReadOnly(True) if wx.Platform == '__WXMSW__': font = "Courier New" else: font = "Courier" self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font)) self._textCtrl.SetFontColor(wx.BLACK) self._textCtrl.StyleClearAll() wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick) self.SetSizer(sizer) sizer.Fit(self) self._stopped = False # Executor initialization self._executor = Executor(fileName, self, callbackOnExit=self.ExecutorFinished) self.Bind(EVT_UPDATE_STDTEXT, self.AppendText) self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText) RunCommandUI.runners.append(self) def __del__(self): # See comment on PythonDebuggerUI.StopExecution self._executor.DoStopExecution() def Execute(self, initialArgs, startIn, environment, onWebServer = False): self._executor.Execute(initialArgs, startIn, environment) def ExecutorFinished(self): self._tb.EnableTool(self.KILL_PROCESS_ID, False) nb = self.GetParent() for i in range(0,nb.GetPageCount()): if self == nb.GetPage(i): text = nb.GetPageText(i) newText = text.replace("Running", "Finished") nb.SetPageText(i, newText)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -