Shutdown subprocess debugger and associated Proxies/Adapters when closing

the Idle debugger.

M PyShell.py       : Call RemoteDebugger.close_remote_debugger()
M RemoteDebugger.py: Add close_remote_debugger(); further polish code used
                     to start the debugger sections.
M rpc.py           : Add comments on Idlefork methods register(), unregister()
                     comment out unused methods
M run.py           : Add stop_the_debugger(); polish code
This commit is contained in:
Kurt B. Kaiser 2002-06-26 02:32:09 +00:00
parent fdc34315f7
commit ffd3a4217a
4 changed files with 78 additions and 39 deletions

View file

@ -25,6 +25,7 @@ from configHandler import idleConf
import idlever import idlever
import rpc import rpc
import RemoteDebugger
# XX hardwire this for now, remove later KBK 09Jun02 # XX hardwire this for now, remove later KBK 09Jun02
use_subprocess = 1 # Set to 1 to spawn subprocess for command execution use_subprocess = 1 # Set to 1 to spawn subprocess for command execution
@ -89,8 +90,7 @@ linecache.checkcache = linecache_checkcache
class PyShellEditorWindow(EditorWindow): class PyShellEditorWindow(EditorWindow):
"Regular text edit window when a shell is present"
# Regular text edit window when a shell is present
# XXX ought to merge with regular editor window # XXX ought to merge with regular editor window
def __init__(self, *args): def __init__(self, *args):
@ -532,6 +532,8 @@ class PyShell(OutputWindow):
if db: if db:
self.interp.setdebugger(None) self.interp.setdebugger(None)
db.close() db.close()
if self.interp.rpcclt:
RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
self.resetoutput() self.resetoutput()
self.console.write("[DEBUG OFF]\n") self.console.write("[DEBUG OFF]\n")
sys.ps1 = ">>> " sys.ps1 = ">>> "
@ -551,7 +553,6 @@ class PyShell(OutputWindow):
self.set_debugger_indicator() self.set_debugger_indicator()
def open_remote_debugger(self): def open_remote_debugger(self):
import RemoteDebugger
gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, self) gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, self)
self.interp.setdebugger(gui) self.interp.setdebugger(gui)
sys.ps1 = "[DEBUG ON]\n>>> " sys.ps1 = "[DEBUG ON]\n>>> "
@ -559,7 +560,7 @@ class PyShell(OutputWindow):
self.set_debugger_indicator() self.set_debugger_indicator()
def beginexecuting(self): def beginexecuting(self):
# Helper for ModifiedInterpreter "Helper for ModifiedInterpreter"
self.resetoutput() self.resetoutput()
self.executing = 1 self.executing = 1
##self._cancel_check = self.cancel_check ##self._cancel_check = self.cancel_check

View file

@ -52,6 +52,7 @@ class GUIProxy:
self.oid = gui_adap_oid self.oid = gui_adap_oid
def interaction(self, message, frame, info=None): def interaction(self, message, frame, info=None):
# calls rpc.SocketIO.remotecall() via run.MyHandler instance
self.conn.remotecall(self.oid, "interaction", self.conn.remotecall(self.oid, "interaction",
(message, wrap_frame(frame), wrap_info(info)), (message, wrap_frame(frame), wrap_info(info)),
{}) {})
@ -156,20 +157,21 @@ class IdbAdapter:
#----------end class IdbAdapter---------- #----------end class IdbAdapter----------
def start_debugger(conn, gui_adap_oid): def start_debugger(rpchandler, gui_adap_oid):
"""Start the debugger and its RPC link in the Python subprocess """Start the debugger and its RPC link in the Python subprocess
Start the subprocess side of the split debugger and set up that side of the Start the subprocess side of the split debugger and set up that side of the
RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
objects and linking them together. Register the IdbAdapter to handle RPC objects and linking them together. Register the IdbAdapter with the
requests from the split debugger GUI via the IdbProxy. RPCServer to handle RPC requests from the split debugger GUI via the
IdbProxy.
""" """
gui_proxy = GUIProxy(conn, gui_adap_oid) gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
idb = Debugger.Idb(gui_proxy) idb = Debugger.Idb(gui_proxy)
idb_adap = IdbAdapter(idb) idb_adap = IdbAdapter(idb)
idb_adap_oid = "idb_adapter" idb_adap_oid = "idb_adapter"
conn.register(idb_adap_oid, idb_adap) rpchandler.register(idb_adap_oid, idb_adap)
return idb_adap_oid return idb_adap_oid
@ -315,25 +317,39 @@ class IdbProxy:
msg = self.call("clear_all_file_breaks", filename) msg = self.call("clear_all_file_breaks", filename)
def start_remote_debugger(conn, pyshell): def start_remote_debugger(rpcclt, pyshell):
"""Start the subprocess debugger, initialize the debugger GUI and RPC link """Start the subprocess debugger, initialize the debugger GUI and RPC link
Request the RPCServer start the Python subprocess debugger and link. Set Request the RPCServer start the Python subprocess debugger and link. Set
up the Idle side of the split debugger by instantiating the IdbProxy, up the Idle side of the split debugger by instantiating the IdbProxy,
debugger GUI, and debugger GUIAdapter objects and linking them together. debugger GUI, and debugger GUIAdapter objects and linking them together.
Register the GUIAdapter to handle debugger GUI interaction requests coming Register the GUIAdapter with the RPCClient to handle debugger GUI
from the subprocess debugger via the GUIProxy. interaction requests coming from the subprocess debugger via the GUIProxy.
The IdbAdapter will pass execution and environment requests coming from the The IdbAdapter will pass execution and environment requests coming from the
Idle debugger GUI to the subprocess debugger via the IdbProxy. Idle debugger GUI to the subprocess debugger via the IdbProxy.
""" """
gui_adap_oid = "gui_adapter" gui_adap_oid = "gui_adapter"
idb_adap_oid = conn.remotecall("exec", "start_the_debugger",\ idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
(gui_adap_oid,), {}) (gui_adap_oid,), {})
idb_proxy = IdbProxy(conn, idb_adap_oid) idb_proxy = IdbProxy(rpcclt, idb_adap_oid)
gui = Debugger.Debugger(pyshell, idb_proxy) gui = Debugger.Debugger(pyshell, idb_proxy)
gui_adap = GUIAdapter(conn, gui) gui_adap = GUIAdapter(rpcclt, gui)
conn.register(gui_adap_oid, gui_adap) rpcclt.register(gui_adap_oid, gui_adap)
return gui return gui
def close_remote_debugger(rpcclt):
"""Shut down subprocess debugger and Idle side of debugger RPC link
Request that the RPCServer shut down the subprocess debugger and link.
Unregister the GUIAdapter, which will cause a GC on the Idle process
debugger and RPC link objects. (The second reference to the debugger GUI
is deleted in PyShell.close_remote_debugger().)
"""
idb_adap_oid = "idb_adapter"
rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
gui_adap_oid = "gui_adapter"
rpcclt.unregister(gui_adap_oid)

View file

@ -55,25 +55,48 @@ class RPCServer(SocketServer.TCPServer):
def __init__(self, addr, handlerclass=None): def __init__(self, addr, handlerclass=None):
if handlerclass is None: if handlerclass is None:
handlerclass = RPCHandler handlerclass = RPCHandler
self.objtable = objecttable # XXX KBK 25Jun02 Not used in Idlefork, see register/unregister note below.
# self.objtable = objecttable
SocketServer.TCPServer.__init__(self, addr, handlerclass) SocketServer.TCPServer.__init__(self, addr, handlerclass)
def verify_request(self, request, client_address): # XXX KBK 25Jun02 Following method is not used (yet)
host, port = client_address # def verify_request(self, request, client_address):
if host != "127.0.0.1": # host, port = client_address
print "Disallowed host:", host # if host != "127.0.0.1":
return 0 # print "Disallowed host:", host
else: # return 0
return 1 # else:
# return 1
def register(self, oid, object): # XXX KBK 25Jun02 The handlerclass is expected to provide register/unregister
self.objtable[oid] = object # methods. In Idle, RPCServer is instantiated with
# handlerclass MyHandler, which in turn inherits the
# register/unregister methods from the mix-in class SocketIO.
# It is true that this is asymmetric with the RPCClient's use
# of register/unregister, but I guess that's how a SocketServer
# is supposed to work.
def unregister(self, oid): # Exactly how this gets set up is convoluted. When the
try: # TCPServer is instantiated, it creates an instance of
del self.objtable[oid] # run.MyHandler and calls its handle() method. handle()
except KeyError: # instantiates a run.Executive, passing it a reference to the
pass # MyHandler object. That reference is saved as an attribute of
# the Executive instance. The Executive methods have access to
# the reference and can pass it on to entities that they
# command (e.g. RemoteDebugger.Debugger.start_debugger()). The
# latter, in turn, can call MyHandler(SocketIO)
# register/unregister methods via the reference to register and
# unregister themselves. Whew.
# The following two methods are not currently used in Idlefork.
# def register(self, oid, object):
# self.objtable[oid] = object
# def unregister(self, oid):
# try:
# del self.objtable[oid]
# except KeyError:
# pass
objecttable = {} objecttable = {}
@ -198,11 +221,6 @@ class SocketIO:
pass pass
else: else:
raise getattr(__import__(mod), name)(*args) raise getattr(__import__(mod), name)(*args)
# XXX KBK 15Jun02 mod is False here, also want to raise remaining exceptions
# else:
# if mod:
# name = mod + "." + name
# raise name, args
raise name, args raise name, args
if how == "ERROR": if how == "ERROR":
raise RuntimeError, what raise RuntimeError, what

View file

@ -23,7 +23,7 @@ class MyHandler(rpc.RPCHandler):
class Executive: class Executive:
def __init__(self, rpchandler): def __init__(self, rpchandler):
self.conn = rpchandler self.rpchandler = rpchandler
import __main__ import __main__
self.locals = __main__.__dict__ self.locals = __main__.__dict__
@ -32,14 +32,18 @@ class Executive:
def start_the_debugger(self, gui_adap_oid): def start_the_debugger(self, gui_adap_oid):
import RemoteDebugger import RemoteDebugger
return RemoteDebugger.start_debugger(self.conn, gui_adap_oid) return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
def stop_the_debugger(self, idb_adap_oid):
"Unregister the Idb Adapter. Link objects and Idb then subject to GC"
self.rpchandler.unregister(idb_adap_oid)
def stackviewer(self, flist_oid=None): def stackviewer(self, flist_oid=None):
if not hasattr(sys, "last_traceback"): if not hasattr(sys, "last_traceback"):
return None return None
flist = None flist = None
if flist_oid is not None: if flist_oid is not None:
flist = self.conn.get_remote_proxy(flist_oid) flist = self.rpchandler.get_remote_proxy(flist_oid)
import RemoteObjectBrowser import RemoteObjectBrowser
import StackViewer import StackViewer
tb = sys.last_traceback tb = sys.last_traceback