# -*- coding: utf-8 -*-
# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>
#
"""
Module implementing the Qt version of the debug client.
"""
import sys
import socket
import select
import codeop
import traceback
import bdb
import os
import types
import string
from qt import PYSIGNAL
from DebugProtocol import *
from AsyncIO import *
from Config import ConfigVarTypeStrings
import PyCoverage
import PyProfile
DebugClientInstance = None
def printerr(s):
"""
Module function used for debugging the debug client.
Arguments
s -- the data to be printed
"""
sys.__stderr__.write('%s\n' % str(s))
sys.__stderr__.flush()
def DebugClientQAppHook():
"""
Module function called by PyQt when the QApplication instance has been created.
"""
if DebugClientInstance is not None:
AsyncIO.setNotifiers(DebugClientInstance)
# Make the hook available to PyQt.
try:
__builtins__.__dict__['__pyQtQAppHook__'] = DebugClientQAppHook
except:
import __main__
__main__.__builtins__.__dict__['__pyQtQAppHook__'] = DebugClientQAppHook
def DebugClientRawInput(prompt):
"""
Replacement for the standard raw_input builtin.
This function works with the Qt event loop.
"""
if DebugClientInstance is None:
return DebugClientOrigRawInput(prompt)
return DebugClientInstance.raw_input(prompt)
# Use our own raw_input().
try:
DebugClientOrigRawInput = __builtins__.__dict__['raw_input']
__builtins__.__dict__['raw_input'] = DebugClientRawInput
except:
DebugClientOrigRawInput = __main__.__builtins__.__dict__['raw_input']
__main__.__builtins__.__dict__['raw_input'] = DebugClientRawInput
class DebugClient(AsyncIO,bdb.Bdb):
"""
Class implementing the client side of the debugger.
It provides access to the Python interpeter from a debugger running in another
process whether or not the Qt event loop is running.
The protocol between the debugger and the client assumes that there will be
a single source of debugger commands and a single source of Python
statements. Commands and statement are always exactly one line and may be
interspersed.
The protocol is as follows. First the client opens a connection to the
debugger and then sends a series of one line commands. A command is either
>Load<, >Step<, >StepInto<, ... or a Python statement. See DebugProtocol.py
for a listing of valid protocol tokens.
A Python statement consists of the statement to execute, followed (in a
separate line) by >OK?<. If the statement was incomplete then the response
is >Continue<. If there was an exception then the response is >Exception<.
Otherwise the response is >OK<. The reason for the >OK?< part is to
provide a sentinal (ie. the responding >OK<) after any possible output as a
result of executing the command.
The client may send any other lines at any other time which should be
interpreted as program output.
If the debugger closes the session there is no response from the client.
The client may close the session at any time as a result of the script
being debugged closing or crashing.
"""
def __init__(self):
"""
Constructor
"""
AsyncIO.__init__(self)
bdb.Bdb.__init__(self)
# The context to run the debugged program in.
self.context = {'__name__' : '__main__'}
# The list of complete lines to execute.
self.buffer = ''
self.connect(self,PYSIGNAL('lineReady'),self.handleLine)
self.connect(self,PYSIGNAL('gotEOF'),self.sessionClose)
self.pendingResponse = ResponseOK
self.fncache = {}
self.dircache = []
self.inRawMode = 0
self.mainProcStr = None # used for the passive mode
self.passive = 0 # used to indicate the passive mode
self.running = None
self.readstream = None
self.writestream = None
self.errorstream = None
self.stepFrame = None # frame that we are stepping in, can be different than currentFrame
# So much for portability.
if sys.platform == 'win32':
self.skipdir = sys.prefix
else:
self.skipdir = os.path.join(sys.prefix,'lib/python' + sys.version[0:3])
def raw_input(self,prompt):
"""
Public method to implement raw_input() using the event loop.
Arguments
prompt -- the prompt to be shown (string)
Returns
the entered string
"""
self.write("%s%s\n" % (ResponseRaw, prompt))
self.inRawMode = 1
self.eventLoop()
return self.rawLine
def handleException(self):
"""
Private method called in the case of an exception
It ensures that the debug server is informed of the raised exception.
"""
self.pendingResponse = ResponseException
def dispatch_return(self, frame, arg):
"""
Reimplemented from bdb.py to handle passive mode cleanly.
"""
if self.stop_here(frame) or frame == self.returnframe:
self.user_return(frame, arg)
if self.quitting and not self.passive: raise bdb.BdbQuit
return self.trace_dispatch
def dispatch_exception(self, frame, arg):
"""
Reimplemented from bdb.py to always call user_exception.
"""
self.user_exception(frame, arg)
if self.quitting: raise bdb.BdbQuit
return self.trace_dispatch
def set_continue(self):
"""
Reimplemented from bdb.py to always get informed of exceptions.
"""
# Modified version of the one found in bdb.py
# Here we leave tracing switched on in order to get
# informed of exceptions
self.stopframe = self.botframe
self.returnframe = None
self.quitting = 0
def fix_frame_filename(self, frame):
"""
Protected method used to fixup the filename for a given frame.
The logic employed here is that if a module was loaded
from a .pyc file, then the correct .py to operate with
should be in the same path as the .pyc. The reason this
logic is needed is that when a .pyc file is generated, the
filename embedded and thus what is readable in the code object
of the frame object is the fully qualified filepath when the
pyc is generated. If files are moved from machine to machine
this can break debugging as the .pyc will refer to the .py
on the original machine. Another case might be sharing
code over a network... This logic deals with that.
Arguments
frame -- the frame object
"""
# get module name from __file__
if frame.f_globals.has_key('__file__'):
root, ext = os.path.splitext(frame.f_globals['__file__'])
if ext == '.pyc' or ext == '.py':
fixedName = root + '.py'
if os.path.exists(fixedName):
return fixedName
return frame.f_code.co_filename
def break_here(self, frame):
"""
Reimplemented from bdb.py to fix the filename from the frame.
See fix_frame_filename for more info.
Arguments
frame -- the frame object
Returns
flag indicating the break status (boolean)
"""
filename = self.canonic(self.fix_frame_filename(frame))
if not self.breaks.has_key(filename):
return 0
lineno = frame.f_lineno
if not lineno in self.breaks[filename]:
return 0
# flag says ok to delete temp. bp
(bp, flag) = bdb.effective(filename, lineno, frame)
if bp:
self.currentbp = bp.number
if (flag and bp.temporary):
self.do_clear(str(bp.number))
return 1
else:
return 0
def break_anywhere(self, frame):
"""
Reimplemented from bdb.py to fix the filename from the frame.
See fix_frame_filename for more info.
Arguments
frame -- the frame object
Returns
flag indicating the break status (boolean)
"""
return self.breaks.has_key(
self.canonic(self.fix_frame_filename(frame)))
def sessionClose(self):
"""
Private method to close the session with the debugger and terminate.
"""
try:
self.set_quit()
except:
pass
# clean up asyncio.
self.disconnect()
# make sure we close down our end of the socket
# might be overkill as normally stdin, stdout and stderr
# SHOULD be closed on exit, but it does not hurt to do it here
self.readstream.close()
self.writestream.close()
self.errorstream.close()
# Ok, go away.
sys.exit()
def handleLine(self,line):
"""
Private method to handle the receipt of a complete line.
It first looks for a valid protocol token at the start of the line. Thereafter
it trys to execute the lines accumulated so far.
Arguments
line -- the received line
"""
# Remove any newline.
if line[-1] == '\n':
line = line[:-1]
##~ printerr(line) ##debug
eoc = line.find('<')
if eoc >= 0 and line[0] == '>':
# Get the command part and any argument.
cmd = line[:eoc + 1]
arg = line[eoc + 1:]
if cmd == RequestVariables:
frmnr, scope, filter = eval(arg)
self.dumpVariables(int(frmnr), int(scope), filter)
return
if cmd == RequestStep:
self.stepFrame = self.currentFrame
self.currentFrame = None
self.set_step()
self.eventExit = 1
return
if cmd == RequestStepOver:
self.stepFrame = self.currentFrame
self.set_next(self.currentFrame)
self.eventExit = 1
return
if cmd == RequestStepOut:
self.stepFrame = self.currentFrame
self.set_return(self.currentFrame)
self.eventExit = 1
return
if cmd == RequestStepQuit:
if self.passive:
self.progTerminated(42)
else:
self.currentFrame = None
self.set_quit()
self.eventExit = 1
return
if cmd == RequestContinue:
self.currentFrame = None
self.set_continue()
self.eventExit = 1
return
if cmd == RequestOK:
self.write(self.pendingResponse + '\n')
self.pendingResponse = ResponseOK
return
if cmd == RequestLoad:
self.fncache = {}
self.dircache = []
sys.argv = []
wd, fn, args = arg.split('|')
sys.argv.append(fn)
sys.argv.extend(args.split())
sys.path[0] = os.path.dirname(sys.argv[0])
if wd == '':
os.chdir(sys.path[0])
else:
os.chdir(wd)
self.running = sys.argv[0]
self.firstFrame = None
self.currentFrame = None
self.inRawMode = 0
# set the system exception handling function to ensure, that
# we report on all unhandled exceptions
sys.excepthook = self.unhandled_exception
# This will eventually enter a local event loop.
# Note the use of backquotes to cause a repr of self.running. The
# need for this is on Windows os where backslash is the path separator.
# They will get inadvertantly stripped away during the eval causing IOErrors
# if self.running is passed as a normal str.
self.run('execfile(' + `self.running` + ')',self.context)
return
if cmd == RequestRun:
sys.argv = []
wd, fn, args = arg.split('|')
sys.argv.append(fn)
sys.argv.extend(args.split())
sys.path[0] = os.path.dirname(sys.argv[0])
if wd == '':
os.chdir(sys.path[0])
else:
os.chdir(wd)
# set the system exception handling function to ensure, that
# we report on all unhandled exceptions
sys.excepthook = self.unhandled_exception
execfile(sys.argv[0], self.context)
return
if cmd == RequestCoverage:
sys.argv = []
wd, fn, args, erase = arg.split('|')
sys.argv.append(fn)
sys.argv.extend(args.split())
sys.path[0] = os.path.dirname(sys.argv[0])
if wd == '':
os.chdir(sys.path[0])
else:
os.chdir(wd)
# set the system exception handling function to ensure, that
# we report on all unhandled exceptions
sys.excepthook = self.unhandled_exception
# generate a coverage object
self.cover = PyCoverage.coverage(sys.argv[0])
# register an exit handler to save the collected data
try:
import atexit
atexit.register(self.cover.save)
except ImportError:
sys.exitfunc = self.cover.save
if int(erase):
self.cover.erase()
self.cover.start()
execfile(sys.argv[0], self.context)
return
if cmd == RequestProfile:
sys.argv = []
wd, fn, args, erase = arg.split('|')
sys.argv.append(fn)
sys.argv.extend(args.split())
sys.path[0] = os.path.dirname(sys.argv[0])
if wd == '':
os.chdir(sys.path[0])
else:
os.chdir(wd)
# set the system exception handling function to ensure, that
# we report on all unhandled exceptions
sys.excepthook = self.unhandled_exception
# generate a profile object
self.prof = PyProfile.PyProfile(sys.argv[0])
if int(erase):
self.prof.erase()
self.prof.run('execfile(' + `sys.argv[0]` + ',' + `self.context` + ')')
return
if cmd == RequestShutdown:
self.sessionClose()
return
if cmd == RequestBreak:
fn, line, set, cond = arg.split(',')
line = int(line)
set = int(set)
if set:
if cond == 'None':
cond = None
self.set_break(fn,line, 0, cond)
else:
self.clear_break(fn,line)
return
if cmd == RequestEval:
try:
value = eval(arg, self.currentFrame.f_globals,
self.currentFrame.f_locals)
except:
# Report the exception and the traceback
try:
type, value, tb = sys.exc_info()
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
tblist = traceback.extract_tb(tb)
del tblist[:1]
list = traceback.format_list(tblist)
if list:
list.insert(0, "Traceback (innermost last):\n")
list[len(list):] = traceback.format_exception_only(type, value)
finally:
tblist = tb = None
map(self.write,list)
self.write(ResponseException + '\n')
else:
self.write(str(value) + '\n')
self.write(ResponseOK + '\n')
return
if cmd == RequestExec:
globals = self.currentFrame.f_globals
locals = self.currentFrame.f_locals
try:
code = compile(arg + '\n', '<stdin>', 'single')
exec code in globals, locals
except:
# Report the exception and the traceback
try:
type, value, tb = sys.exc_info()
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
tblist = traceback.extract_tb(tb)
del tblist[:1]
list = traceback.format_list(tblist)
if list:
list.insert(0, "Traceback (innermost last):\n")
list[len(list):] = traceback.format_exception_only(type, value)
finally:
tblist = tb = None
map(self.write,list)
self.write(ResponseException + '\n')
return
if cmd == RequestBanner:
self.write('%s%s\n' % (ResponseBanner,
str((sys.version, sys.platform, 'Qt-Version'))))
return
# If we are handling raw mode input then reset the mode and break out
# of the current event loop.
if self.inRawMode:
self.inRawMode = 0
self.rawLine = line
self.eventExit = 1
return
if self.buffer:
self.buffer = self.buffer + '\n' + line
else:
self.buffer = line
try:
code = codeop.compile_command(self.buffer,self.readstream.name)
except (OverflowError, SyntaxError, ValueError):
# Report the exception
sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
map(self.write,traceback.format_exception_only(sys.last_type,sys.last_value))
self.buffer = ''
self.handleException()
else:
if code is None:
self.pendingResponse = ResponseContinue
else:
self.buffer = ''
try:
if self.running is None:
exec code in self.context
else:
globals = self.currentFrame.f_globals
locals = self.currentFrame.f_locals
exec code in globals, locals
except SystemExit:
self.sessionClose()
except:
# Report the exception and the traceback
try:
type, value, tb = sys.exc_info()
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
tblist = traceback.extract_tb(tb)
del tblist[:1]
list = traceback.format_list(tblist)
if list:
list.insert(0, "Traceback (innermost last):\n")
list[len(list):] = traceback.format_exception_only(type, value)
finally:
tblist = tb = None
map(self.write,list)
self.handleException()
def write(self,s):
"""
Private method to write data to the output stream.
Arguments
s -- data to be written (string)
"""
self.writestream.write(s)
self.writestream.flush()
def interact(self):
"""
Private method to Interact with the debugger.
"""
# Set the descriptors now and the notifiers when the QApplication
# instance is created.
global DebugClientInstance
self.setDescriptors(self.readstream,self.writestream)
DebugClientInstance = self
if not self.passive:
# At this point the Qt event loop isn't running, so simulate it.
self.eventLoop()
def eventLoop(self):
"""
Private method implementing our event loop.
"""
self.eventExit = None
while self.eventExit is None:
wrdy = []
if AsyncPendingWrite(self.writestream):
wrdy.append(self.writestream)
if AsyncPendingWrite(self.errorstream):
wrdy.append(self.errorstream)
rrdy, wrdy, xrdy = select.select([self.readstream],wrdy,[])
if self.readstream in rrdy:
self.readReady(self.readstream.fileno())
if self.writestream in wrdy:
self.writeReady(self.writestream.fileno())
if self.errorstream in wrdy:
self.writeReady(self.errorstream.fileno())
self.eventExit = None
def connectDebugger(self,port,remoteAddress=None):
"""
Public method to establish a session with the debugger.
It opens a network connection to the debugger, connects it to stdin,
stdout and stderr and saves these file objects in case the application
being debugged redirects them itself.
Arguments
port -- the port number to connect to (int)
remoteAddress -- the network address of the debug server host (string)
"""
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
if remoteAddress is None:
sock.connect((DebugAddress,port))
else:
sock.connect((remoteAddress,port))
sock.setblocking(0)
sys.stdin = AsyncFile(sock,sys.stdin.mode,sys.stdin.name)
sys.stdout = AsyncFile(sock,sys.stdout.mode,sys.stdout.name)
sys.stderr = AsyncFile(sock,sys.stderr.mode,sys.stderr.name)
# save the stream in case a program redirects them
self.readstream = sys.stdin
self.writestream = sys.stdout
self.errorstream = sys.stderr
def user_line(self,frame):
"""
Reimplemented to handle the program about to execute a particular line.
Arguments
frame -- the frame object
"""
line = frame.f_lineno
# We never stop an line 0.
if line == 0:
return
fn = self.absPath(self.fix_frame_filename(frame))
# See if we are skipping at the start of a newly loaded program.
if self.firstFrame is None:
if fn != self.running:
return
self.firstFrame = frame
self.currentFrame = frame
fr = frame
stack = []
while fr is not None:
#
# Reset the trace function so we can be sure
# to trace all functions up the stack... This gets around
# problems where an exception/breakpoint has occurred
# but we had disabled tracing along the way via a None
# return from dispatch_call
fr.f_trace = self.trace_dispatch
fname = self.absPath(self.fix_frame_filename(fr))
fline = fr.f_lineno
ffunc = fr.f_code.co_name
if ffunc == '?':
ffunc = ''
stack.append((fname, fline, ffunc))
if fr == self.firstFrame:
fr = None
else:
fr = fr.f_back
self.write('%s%s\n' % (ResponseLine,str(stack)))
self.eventLoop()
def unhandled_exception(self, exctype, excval, exctb):
"""
Private method called to report an uncaught exception.
Arguments
exctype -- the type of the exception
excval -- data about the exception
exctb -- traceback for the exception
"""
self.user_exception(None, (exctype,excval,exctb), 1)
def user_exception(self,frame,(exctype,excval,exctb),unhandled=0):
"""
Reimplemented to report an exception to the debug server.
Arguments
frame -- the frame object
exctype -- the type of the exception
excval -- data about the exception
exctb -- traceback for the exception
unhandled -- flag indicating an uncaught exception
"""
if exctype == SystemExit:
self.progTerminated(excval)
elif exctype in [SyntaxError, IndentationError]:
try:
message, (filename, linenr, charnr, text) = excval
except:
exclist = []
else:
exclist = [message, (filename, linenr, charnr)]
self.write("%s%s\n" % (ResponseSyntax, str(exclist)))
else:
self.currentFrame = frame
if type(exctype) == types.ClassType:
exctype = exctype.__name__
if excval is None:
excval = ''
if unhandled:
exclist = ["unhandled %s" % str(exctype), str(excval)]
else:
exclist = [str(exctype), str(excval)]
frlist = self.extract_stack(exctb)
frlist.reverse()
for fr in frlist:
filename = self.absPath(self.fix_frame_filename(fr))
linenr = fr.f_lineno
exclist.append((filename, linenr))
self.write("%s%s\n" % (ResponseException, str(exclist)))
self.eventLoop()
def extract_stack(self, exctb):
"""
Protected member to return a list of stack frames.
Arguments
exctb -- exception traceback
Returns
list of stack frames
"""
tb = exctb
stack = []
while tb is not None:
stack.append(tb.tb_frame)
tb = tb.tb_next
tb = None
return stack
def user_return(self,frame,retval):
"""
Reimplemented to report program termination to the debug server.
Arguments
frame -- the frame object
retval -- the return value of the program
"""
# The program has finished if we have just left the first frame.
if frame == self.firstFrame:
self.progTerminated(retval)
elif frame is not self.stepFrame:
self.stepFrame = None
self.user_line(frame)
def stop_here(self,frame):
"""
Reimplemented to filter out debugger files.
Tracing is turned off for files that are part of the
debugger that are called from the application being debugged.
Arguments
frame -- the frame object
Returns
flag indicating whether the debugger should stop here
"""
fn = self.fix_frame_filename(frame)
# Eliminate things like <string> and <stdin>.
if fn[0] == '<':
return 0
#XXX - think of a better way to do this. It's only a convience for
#debugging the debugger - when the debugger code is in the current
#directory.
if os.path.basename(fn) in ['AsyncIO.py', 'DebugClient.py']:
return 0
# Eliminate anything that is part of the Python installation.
#XXX - this should be a user option, or an extension of the meaning of
# 'step into', or a separate type of step altogether, or have a
#configurable set of ignored directories.
afn = self.absPath(fn)
if self.skipdir is not None and afn.find(self.skipdir) == 0:
return 0
return bdb.Bdb.stop_here(self,frame)
def absPath(self,fn):
"""
Private method to convert a filename to an absolute name.
sys.path is used as a set of possible prefixes. The name stays
relative if a file could not be found.
Arguments
fn -- filename (string)
Returns
the converted filename (string)
"""
if os.path.isabs(fn):
return fn
# Check the cache.
if self.fncache.has_key(fn):
return self.fncache[fn]
# Search sys.path.
for p in sys.path:
afn = os.path.abspath(os.path.join(p,fn))
if os.path.exists(afn):
self.fncache[fn] = afn
d = os.path.dirname(afn)
if (d not in sys.path) and (d not in self.dircache):
self.dircache.append(d)
return afn
# Search the additional directory cache
for p in self.dircache:
afn = os.path.abspath(os.path.join(p,fn))
if os.path.exists(afn):
self.fncache[fn] = afn
return afn
# Nothing found.
return fn
def progTerminated(self,status):
"""
Private method to tell the debugger that the program has terminated.
Arguments
status -- the return status
"""
if status is None:
status = 0
else:
try:
int(status)
except:
status = 1
self.set_quit()
self.running = None
self.write('%s%d\n' % (ResponseExit,status))
def dumpVariables(self, frmnr, scope, filter):
"""
Private method to return the variables of a frame to the debug server.
Arguments
frmnr -- distance of frame reported on. 0 is the current frame (int)
scope -- 1 to report global variables, 0 for local variables (int)
filter -- the indices of variable types to be filtered (list of int)
"""
f = self.currentFrame
while f is not None and frmnr > 0:
f = f.f_back
frmnr -= 1
if f is None:
return
if scope:
dict = f.f_globals
else:
dict = f.f_locals
if f.f_globals is f.f_locals:
scope = -1
varlist = [scope]
if scope != -1:
keylist = dict.keys()
(vlist, klist) = self.formatVariablesList(keylist, dict, filter)
varlist = varlist + vlist
if len(klist):
for key in klist:
cdict = None
try:
cdict = dict[key].__dict__
except:
try:
slv = dict[key].__slots__
cdict = {}
for v in slv:
exec 'cdict[v] = dict[key].%s' % v
except:
pass
if cdict is not None:
keylist = cdict.keys()
(vlist, clist) = \
self.formatVariablesList(keylist, cdict, filter, 1, key)
varlist = varlist + vlist
self.write('%s%s\n' % (ResponseVariables, str(varlist)))
def formatVariablesList(self, keylist, dict, filter = [], classdict = 0, prefix = ''):
"""
Private method to produce a formated variables list.
The dictionary passed in to it is scanned. If classdict is false,
it builds a list of all class instances in dict. If it is
true, we are formatting a class dictionary. In this case
we prepend prefix to the variable names. Variables are
only added to the list, if their type is not contained
in the filter list. The formated variables list (a list of
tuples of 3 values) and the list of class instances is returned.
Arguments
keylist -- keys of the dictionary
dict -- the dictionary to be scanned
filter -- the indices of variable types to be filtered. Variables are
only added to the list, if their type is not contained
in the filter list.
classdict -- boolean indicating the formating of a class or
module dictionary. If classdict is false,
it builds a list of all class instances in dict. If it is
true, we are formatting a class dictionary. In this case
we prepend prefix to the variable names.
prefix -- prefix to prepend to the variable names (string)
Returns
A tuple consisting of a list of formatted variables and a list of
class instances. Each variable entry is a tuple of three elements,
the variable name, its type and value.
"""
varlist = []
classes = []
for key in keylist:
# filter hidden attributes (filter #0)
if 0 in filter and str(key)[:2] == '__':
continue
# special handling for '__builtins__' (it's way too big)
if key == '__builtins__':
rvalue = '<module __builtin__ (built-in)>'
valtype = 'module'
else:
value = dict[key]
valtypestr = str(type(value))[1:-1]
if string.split(valtypestr,' ',1)[0] == 'class':
# handle new class type of python 2.2+
if ConfigVarTypeStrings.index('instance') in filter:
continue
valtype = valtypestr[7:-1]
classes.append(key)
else:
valtype = valtypestr[6:-1]
try:
if ConfigVarTypeStrings.index(valtype) in filter:
continue
if valtype in ['instance', 'module']:
classes.append(key)
except ValueError:
if ConfigVarTypeStrings.index('other') in filter:
continue
try:
rvalue = repr(value)
except:
rvalue = ''
if classdict:
key = prefix + '.' + key
varlist.append((key, valtype, rvalue))
return (varlist, classes)
def startDebugger(self, filename=None, host=None, port=None):
"""
Method used to start the remote debugger.
Arguments
filename -- the program to be debugged (string)
host -- hostname of the debug server (string)
port -- portnumber of the debug server (int)
"""
global debugClient
if host is None:
host = os.getenv('ERICHOST', 'localhost')
if port is None:
port = os.getenv('ERICPORT', 42424)
self.connectDebugger(port, socket.gethostbyname(host))
if filename is not None:
self.running = os.path.abspath(filename)
else:
try:
self.running = os.path.abspath(sys.argv[0])
except:
pass
self.passive = 1
self.write(PassiveStartup + '\n')
self.interact()
# setup the debugger variables
self.fncache = {}
self.dircache = []
self.firstFrame = None
self.currentFrame = None
self.inRawMode = 0
# set the system exception handling function to ensure, that
# we report on all unhandled exceptions
sys.excepthook = self.unhandled_exception
# now start debugging
self.set_trace()
# We are normally called by the debugger to execute directly.
if __name__ == '__main__':
try:
port = int(sys.argv[1])
except:
port = -1
try:
remoteAddress = sys.argv[2]
except:
remoteAddress = None
sys.argv = ['']
sys.path[0] = ''
debugClient = DebugClient()
if port >= 0:
debugClient.connectDebugger(port, remoteAddress)
debugClient.interact()
|