Commit 63857a45 authored by Kurt B. Kaiser's avatar Kurt B. Kaiser

M PyShell.py

M RemoteDebugger.py
M ScriptBinding.py

Restart the execution server with a clean environment and execute the
active module from scratch upon activation of Run/F5.

Add functionality to PyShell.py to restart the execution server in a new
subprocess.  The server makes a connection to the Idle client which sends a
block of code to be executed.

Modify ScriptBinding.py to restart the subprocess upon Run/F5, assuming that
an execution is not currently in progress.  Remove Import Module functionality,
not required now that the code is executed in a clean environment.

If the Debugger is active, also restart the subprocess side of the split
debugger.  Add functionality to RemoteDebugger.py to support this.

At this time breakpoints will be lost in the subprocess if Run/F5 is activated.
A subsequent checkin of PyShell.py will implement reloading of the breakpoints
into the subprocess debugger.  I'm keeping this separate as the design may
change.
parent 342456d5
...@@ -187,17 +187,19 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -187,17 +187,19 @@ class ModifiedInterpreter(InteractiveInterpreter):
InteractiveInterpreter.__init__(self, locals=locals) InteractiveInterpreter.__init__(self, locals=locals)
self.save_warnings_filters = None self.save_warnings_filters = None
port = 8833
rpcclt = None rpcclt = None
rpcpid = None rpcpid = None
def spawn_subprocess(self): def spawn_subprocess(self):
port = 8833
addr = ("localhost", port)
# Spawn the Python execution "server"
w = ['-W' + s for s in sys.warnoptions] w = ['-W' + s for s in sys.warnoptions]
args = [sys.executable] + w + ["-c", "__import__('run').main()", args = [sys.executable] + w + ["-c", "__import__('run').main()",
str(port)] str(self.port)]
self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args) self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
def start_subprocess(self):
addr = ("localhost", self.port)
self.spawn_subprocess()
# Idle starts listening for connection on localhost # Idle starts listening for connection on localhost
for i in range(6): for i in range(6):
time.sleep(i) time.sleep(i)
...@@ -221,6 +223,21 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -221,6 +223,21 @@ class ModifiedInterpreter(InteractiveInterpreter):
self.rpcclt.register("flist", self.tkconsole.flist) self.rpcclt.register("flist", self.tkconsole.flist)
self.poll_subprocess() self.poll_subprocess()
def restart_subprocess(self):
# close only the subprocess debugger
db = self.getdebugger()
if db:
RemoteDebugger.close_subprocess_debugger(self.rpcclt)
# kill subprocess, spawn a new one, accept connection
self.rpcclt.close()
self.spawn_subprocess()
self.rpcclt.accept()
# restart remote debugger
if db:
gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
# reload remote debugger breakpoints
pass # XXX KBK 04Sep02 TBD
active_seq = None active_seq = None
def poll_subprocess(self): def poll_subprocess(self):
...@@ -383,15 +400,18 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -383,15 +400,18 @@ class ModifiedInterpreter(InteractiveInterpreter):
def getdebugger(self): def getdebugger(self):
return self.debugger return self.debugger
def display_executing_dialog(self):
tkMessageBox.showerror(
"Already executing",
"The Python Shell window is already executing a command; "
"please wait until it is finished.",
master=self.tkconsole.text)
def runcommand(self, code): def runcommand(self, code):
"Run the code without invoking the debugger" "Run the code without invoking the debugger"
# The code better not raise an exception! # The code better not raise an exception!
if self.tkconsole.executing: if self.tkconsole.executing:
tkMessageBox.showerror( display_executing_dialog()
"Already executing",
"The Python Shell window is already executing a command; "
"please wait until it is finished.",
master=self.tkconsole.text)
return 0 return 0
if self.rpcclt: if self.rpcclt:
self.rpcclt.remotecall("exec", "runcode", (code,), {}) self.rpcclt.remotecall("exec", "runcode", (code,), {})
...@@ -402,11 +422,7 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -402,11 +422,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
def runcode(self, code): def runcode(self, code):
"Override base class method" "Override base class method"
if self.tkconsole.executing: if self.tkconsole.executing:
tkMessageBox.showerror( display_executing_dialog()
"Already executing",
"The Python Shell window is already executing a command; "
"please wait until it is finished.",
master=self.tkconsole.text)
return return
# #
self.checklinecache() self.checklinecache()
...@@ -509,7 +525,7 @@ class PyShell(OutputWindow): ...@@ -509,7 +525,7 @@ class PyShell(OutputWindow):
self.history = self.History(self.text) self.history = self.History(self.text)
if use_subprocess: if use_subprocess:
self.interp.spawn_subprocess() self.interp.start_subprocess()
reading = 0 reading = 0
executing = 0 executing = 0
......
...@@ -27,6 +27,9 @@ import Debugger ...@@ -27,6 +27,9 @@ import Debugger
debugging = 0 debugging = 0
idb_adap_oid = "idb_adapter"
gui_adap_oid = "gui_adapter"
#======================================= #=======================================
# #
# In the PYTHON subprocess: # In the PYTHON subprocess:
...@@ -113,6 +116,7 @@ class IdbAdapter: ...@@ -113,6 +116,7 @@ class IdbAdapter:
def clear_break(self, filename, lineno): def clear_break(self, filename, lineno):
msg = self.idb.clear_break(filename, lineno) msg = self.idb.clear_break(filename, lineno)
return msg
def clear_all_file_breaks(self, filename): def clear_all_file_breaks(self, filename):
msg = self.idb.clear_all_file_breaks(filename) msg = self.idb.clear_all_file_breaks(filename)
...@@ -183,7 +187,6 @@ def start_debugger(rpchandler, gui_adap_oid): ...@@ -183,7 +187,6 @@ def start_debugger(rpchandler, gui_adap_oid):
gui_proxy = GUIProxy(rpchandler, 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"
rpchandler.register(idb_adap_oid, idb_adap) rpchandler.register(idb_adap_oid, idb_adap)
return idb_adap_oid return idb_adap_oid
...@@ -325,6 +328,7 @@ class IdbProxy: ...@@ -325,6 +328,7 @@ class IdbProxy:
def clear_break(self, filename, lineno): def clear_break(self, filename, lineno):
msg = self.call("clear_break", filename, lineno) msg = self.call("clear_break", filename, lineno)
return msg
def clear_all_file_breaks(self, filename): def clear_all_file_breaks(self, filename):
msg = self.call("clear_all_file_breaks", filename) msg = self.call("clear_all_file_breaks", filename)
...@@ -344,7 +348,8 @@ def start_remote_debugger(rpcclt, pyshell): ...@@ -344,7 +348,8 @@ def start_remote_debugger(rpcclt, pyshell):
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" global idb_adap_oid
idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\ idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
(gui_adap_oid,), {}) (gui_adap_oid,), {})
idb_proxy = IdbProxy(rpcclt, idb_adap_oid) idb_proxy = IdbProxy(rpcclt, idb_adap_oid)
...@@ -362,7 +367,14 @@ def close_remote_debugger(rpcclt): ...@@ -362,7 +367,14 @@ def close_remote_debugger(rpcclt):
is deleted in PyShell.close_remote_debugger().) is deleted in PyShell.close_remote_debugger().)
""" """
idb_adap_oid = "idb_adapter" close_subprocess_debugger(rpcclt)
rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
gui_adap_oid = "gui_adapter"
rpcclt.unregister(gui_adap_oid) rpcclt.unregister(gui_adap_oid)
def close_subprocess_debugger(rpcclt):
rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
def restart_subprocess_debugger(rpcclt):
idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
(gui_adap_oid,), {})
assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
...@@ -5,13 +5,9 @@ This adds the following commands: ...@@ -5,13 +5,9 @@ This adds the following commands:
- Check module does a full syntax check of the current module. - Check module does a full syntax check of the current module.
It also runs the tabnanny to catch any inconsistent tabs. It also runs the tabnanny to catch any inconsistent tabs.
- Import module is equivalent to either import or reload of the - Run module executes the module's code in the __main__ namespace. The window
current module. The window must have been saved previously. The must have been saved previously. The module is added to sys.modules, and is
module is added to sys.modules, and is also added to the __main__ also added to the __main__ namespace.
namespace. Output goes to the shell window.
- Run module does the same but executes the module's
code in the __main__ namespace.
XXX Redesign this interface (yet again) as follows: XXX Redesign this interface (yet again) as follows:
...@@ -19,8 +15,6 @@ XXX Redesign this interface (yet again) as follows: ...@@ -19,8 +15,6 @@ XXX Redesign this interface (yet again) as follows:
- Allow specify command line arguments in the dialog box - Allow specify command line arguments in the dialog box
- Restart the interpreter when running a script
""" """
import sys import sys
...@@ -47,7 +41,6 @@ class ScriptBinding: ...@@ -47,7 +41,6 @@ class ScriptBinding:
menudefs = [ menudefs = [
('run', [None, ('run', [None,
# ('Check module', '<<check-module>>'), # ('Check module', '<<check-module>>'),
# ('Import module', '<<import-module>>'),
('Run script', '<<run-script>>'), ('Run script', '<<run-script>>'),
] ]
), ),
...@@ -113,33 +106,6 @@ class ScriptBinding: ...@@ -113,33 +106,6 @@ class ScriptBinding:
"There's an error in your program:\n" + msg) "There's an error in your program:\n" + msg)
return 1 return 1
def import_module_event(self, event):
flist = self.editwin.flist
shell = flist.open_shell()
interp = shell.interp
filename = self.getfilename()
if not filename:
return
modname, ext = os.path.splitext(os.path.basename(filename))
dir = os.path.dirname(filename)
dir = os.path.normpath(os.path.abspath(dir))
interp.runcode("""if 1:
import sys as _sys
if %s not in _sys.path:
_sys.path.insert(0, %s)
if _sys.modules.get(%s):
del _sys
import %s
reload(%s)
else:
del _sys
import %s
\n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
def run_script_event(self, event): def run_script_event(self, event):
filename = self.getfilename() filename = self.getfilename()
if not filename: if not filename:
...@@ -147,6 +113,10 @@ class ScriptBinding: ...@@ -147,6 +113,10 @@ class ScriptBinding:
flist = self.editwin.flist flist = self.editwin.flist
shell = flist.open_shell() shell = flist.open_shell()
interp = shell.interp interp = shell.interp
if interp.tkconsole.executing:
interp.display_executing_dialog()
return
interp.restart_subprocess()
# XXX Too often this discards arguments the user just set... # XXX Too often this discards arguments the user just set...
interp.runcommand("""if 1: interp.runcommand("""if 1:
_filename = %s _filename = %s
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment