📄 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 wx
import wx.lib.intctrl
import wx.lib.docview
import wx.lib.dialogs
import wx.gizmos
import wx._core
import wx.lib.pydocview
import Service
import STCTextEditor
import CodeEditor
import PythonEditor
import PHPEditor
import PHPDebugger
import activegrid.model.projectmodel as projectmodel
from IDE import ACTIVEGRID_BASE_IDE
if not ACTIVEGRID_BASE_IDE:
import ProcessModelEditor
import wx.lib.scrolledpanel as scrolled
import sys
import time
import SimpleXMLRPCServer
import xmlrpclib
import os
import threading
import Queue
import SocketServer
import ProjectEditor
import types
from xml.dom.minidom import parse, parseString
import bz2
import pickle
import DebuggerHarness
import traceback
import StringIO
import UICommon
import activegrid.util.sysutils as sysutilslib
import subprocess
import shutil
if wx.Platform == '__WXMSW__':
try:
import win32api
_PYWIN32_INSTALLED = True
except ImportError:
_PYWIN32_INSTALLED = False
_WINDOWS = True
else:
_WINDOWS = False
if not _WINDOWS or _PYWIN32_INSTALLED:
import process
_ = wx.GetTranslation
_VERBOSE = False
_WATCHES_ON = False
import 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 = False
class 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._path
class 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):
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -