Commit da0702e3 authored by Kurt B. Kaiser's avatar Kurt B. Kaiser

Added a Tk error dialog to run.py inform the user if the subprocess can't

connect to the user GUI process.  Added a timeout to the GUI's listening
socket.  Added Tk error dialogs to PyShell.py to announce a failure to bind
the port or connect to the subprocess.  Clean up error handling during
connection initiation phase.  This is an update of Python Patch 778323.

M NEWS.txt
M PyShell.py
M ScriptBinding.py
M run.py

Backport candidate.
parent 6a3ec456
...@@ -3,6 +3,12 @@ What's New in IDLE 1.1a0? ...@@ -3,6 +3,12 @@ What's New in IDLE 1.1a0?
*Release date: XX-XXX-2004* *Release date: XX-XXX-2004*
- Added a Tk error dialog to run.py inform the user if the subprocess can't
connect to the user GUI process. Added a timeout to the GUI's listening
socket. Added Tk error dialogs to PyShell.py to announce a failure to bind
the port or connect to the subprocess. Clean up error handling during
connection initiation phase. This is an update of Python Patch 778323.
- Print correct exception even if source file changed since shell was - Print correct exception even if source file changed since shell was
restarted. IDLEfork Patch 869012 Noam Raphael restarted. IDLEfork Patch 869012 Noam Raphael
......
...@@ -251,7 +251,9 @@ class PyShellFileList(FileList): ...@@ -251,7 +251,9 @@ class PyShellFileList(FileList):
self.pyshell.wakeup() self.pyshell.wakeup()
else: else:
self.pyshell = PyShell(self) self.pyshell = PyShell(self)
self.pyshell.begin() if self.pyshell:
if not self.pyshell.begin():
return None
return self.pyshell return self.pyshell
...@@ -344,6 +346,9 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -344,6 +346,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
return [decorated_exec] + w + ["-c", command, str(self.port)] return [decorated_exec] + w + ["-c", command, str(self.port)]
def start_subprocess(self): def start_subprocess(self):
# spawning first avoids passing a listening socket to the subprocess
self.spawn_subprocess()
#time.sleep(20) # test to simulate GUI not accepting connection
addr = (LOCALHOST, self.port) addr = (LOCALHOST, self.port)
# Idle starts listening for connection on localhost # Idle starts listening for connection on localhost
for i in range(3): for i in range(3):
...@@ -352,14 +357,17 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -352,14 +357,17 @@ class ModifiedInterpreter(InteractiveInterpreter):
self.rpcclt = MyRPCClient(addr) self.rpcclt = MyRPCClient(addr)
break break
except socket.error, err: except socket.error, err:
print>>sys.__stderr__,"IDLE socket error: " + err[1]\ pass
+ ", retrying..."
else: else:
display_port_binding_error() self.display_port_binding_error()
sys.exit() return None
self.spawn_subprocess()
# Accept the connection from the Python execution server # Accept the connection from the Python execution server
self.rpcclt.accept() self.rpcclt.listening_sock.settimeout(10)
try:
self.rpcclt.accept()
except socket.timeout, err:
self.display_no_subprocess_error()
return None
self.rpcclt.register("stdin", self.tkconsole) self.rpcclt.register("stdin", self.tkconsole)
self.rpcclt.register("stdout", self.tkconsole.stdout) self.rpcclt.register("stdout", self.tkconsole.stdout)
self.rpcclt.register("stderr", self.tkconsole.stderr) self.rpcclt.register("stderr", self.tkconsole.stderr)
...@@ -368,10 +376,11 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -368,10 +376,11 @@ class ModifiedInterpreter(InteractiveInterpreter):
self.rpcclt.register("interp", self) self.rpcclt.register("interp", self)
self.transfer_path() self.transfer_path()
self.poll_subprocess() self.poll_subprocess()
return self.rpcclt
def restart_subprocess(self): def restart_subprocess(self):
if self.restarting: if self.restarting:
return return self.rpcclt
self.restarting = True self.restarting = True
# close only the subprocess debugger # close only the subprocess debugger
debug = self.getdebugger() debug = self.getdebugger()
...@@ -388,7 +397,11 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -388,7 +397,11 @@ class ModifiedInterpreter(InteractiveInterpreter):
was_executing = console.executing was_executing = console.executing
console.executing = False console.executing = False
self.spawn_subprocess() self.spawn_subprocess()
self.rpcclt.accept() try:
self.rpcclt.accept()
except socket.timeout, err:
self.display_no_subprocess_error()
return None
self.transfer_path() self.transfer_path()
# annotate restart in shell window and mark it # annotate restart in shell window and mark it
console.text.delete("iomark", "end-1c") console.text.delete("iomark", "end-1c")
...@@ -407,6 +420,7 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -407,6 +420,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
# reload remote debugger breakpoints for all PyShellEditWindows # reload remote debugger breakpoints for all PyShellEditWindows
debug.load_breakpoints() debug.load_breakpoints()
self.restarting = False self.restarting = False
return self.rpcclt
def __request_interrupt(self): def __request_interrupt(self):
self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
...@@ -415,7 +429,10 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -415,7 +429,10 @@ class ModifiedInterpreter(InteractiveInterpreter):
threading.Thread(target=self.__request_interrupt).start() threading.Thread(target=self.__request_interrupt).start()
def kill_subprocess(self): def kill_subprocess(self):
self.rpcclt.close() try:
self.rpcclt.close()
except AttributeError: # no socket
pass
self.unix_terminate() self.unix_terminate()
self.tkconsole.executing = False self.tkconsole.executing = False
self.rpcclt = None self.rpcclt = None
...@@ -638,13 +655,6 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -638,13 +655,6 @@ class ModifiedInterpreter(InteractiveInterpreter):
if key[:1] + key[-1:] != "<>": if key[:1] + key[-1:] != "<>":
del c[key] del c[key]
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!
...@@ -695,6 +705,34 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -695,6 +705,34 @@ class ModifiedInterpreter(InteractiveInterpreter):
"Override base class method" "Override base class method"
self.tkconsole.stderr.write(s) self.tkconsole.stderr.write(s)
def display_port_binding_error(self):
tkMessageBox.showerror(
"Port Binding Error",
"IDLE can't bind TCP/IP port 8833, which is necessary to "
"communicate with its Python execution server. Either "
"no networking is installed on this computer or another "
"process (another IDLE?) is using the port. Run IDLE with the -n "
"command line switch to start without a subprocess and refer to "
"Help/IDLE Help 'Running without a subprocess' for further "
"details.",
master=self.tkconsole.text)
def display_no_subprocess_error(self):
tkMessageBox.showerror(
"Subprocess Startup Error",
"IDLE's subprocess didn't make connection. Either IDLE can't "
"start a subprocess or personal firewall software is blocking "
"the connection.",
master=self.tkconsole.text)
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)
class PyShell(OutputWindow): class PyShell(OutputWindow):
shell_title = "Python Shell" shell_title = "Python Shell"
...@@ -765,8 +803,6 @@ class PyShell(OutputWindow): ...@@ -765,8 +803,6 @@ class PyShell(OutputWindow):
self.history = self.History(self.text) self.history = self.History(self.text)
# #
self.pollinterval = 50 # millisec self.pollinterval = 50 # millisec
if use_subprocess:
self.interp.start_subprocess()
reading = False reading = False
executing = False executing = False
...@@ -887,6 +923,10 @@ class PyShell(OutputWindow): ...@@ -887,6 +923,10 @@ class PyShell(OutputWindow):
self.resetoutput() self.resetoutput()
if use_subprocess: if use_subprocess:
nosub = '' nosub = ''
client = self.interp.start_subprocess()
if not client:
self.close()
return None
else: else:
nosub = "==== No Subprocess ====" nosub = "==== No Subprocess ===="
self.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" % self.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" %
...@@ -894,11 +934,8 @@ class PyShell(OutputWindow): ...@@ -894,11 +934,8 @@ class PyShell(OutputWindow):
self.firewallmessage, idlever.IDLE_VERSION, nosub)) self.firewallmessage, idlever.IDLE_VERSION, nosub))
self.showprompt() self.showprompt()
import Tkinter import Tkinter
Tkinter._default_root = None Tkinter._default_root = None # 03Jan04 KBK What's this?
return client
def interact(self):
self.begin()
self.top.mainloop()
def readline(self): def readline(self):
save = self.reading save = self.reading
...@@ -1281,11 +1318,9 @@ def main(): ...@@ -1281,11 +1318,9 @@ def main():
flist.open(filename) flist.open(filename)
if not args: if not args:
flist.new() flist.new()
if enable_shell: if enable_shell:
flist.open_shell() if not flist.open_shell():
elif enable_shell: return # couldn't open shell
flist.pyshell = PyShell(flist)
flist.pyshell.begin()
shell = flist.pyshell shell = flist.pyshell
# handle remaining options: # handle remaining options:
if debug: if debug:
...@@ -1295,7 +1330,7 @@ def main(): ...@@ -1295,7 +1330,7 @@ def main():
os.environ.get("PYTHONSTARTUP") os.environ.get("PYTHONSTARTUP")
if filename and os.path.isfile(filename): if filename and os.path.isfile(filename):
shell.interp.execfile(filename) shell.interp.execfile(filename)
if cmd or script: if shell and cmd or script:
shell.interp.runcommand("""if 1: shell.interp.runcommand("""if 1:
import sys as _sys import sys as _sys
_sys.argv = %s _sys.argv = %s
...@@ -1309,24 +1344,6 @@ def main(): ...@@ -1309,24 +1344,6 @@ def main():
root.mainloop() root.mainloop()
root.destroy() root.destroy()
def display_port_binding_error():
print """\
\nIDLE cannot run.
IDLE needs to use a specific TCP/IP port (8833) in order to communicate with
its Python execution server. IDLE is unable to bind to this port, and so
cannot start. Here are some possible causes of this problem:
1. TCP/IP networking is not installed or not working on this computer
2. Another program (another IDLE?) is running that uses this port
3. Personal firewall software is preventing IDLE from using this port
Run IDLE with the -n command line switch to start without a subprocess
and refer to Help/IDLE Help "Running without a subprocess" for further
details.
"""
if __name__ == "__main__": if __name__ == "__main__":
sys.modules['PyShell'] = sys.modules['__main__'] sys.modules['PyShell'] = sys.modules['__main__']
main() main()
...@@ -137,6 +137,8 @@ class ScriptBinding: ...@@ -137,6 +137,8 @@ class ScriptBinding:
return return
flist = self.editwin.flist flist = self.editwin.flist
shell = flist.open_shell() shell = flist.open_shell()
if not shell:
return # couldn't open the shell
interp = shell.interp interp = shell.interp
if PyShell.use_subprocess: if PyShell.use_subprocess:
shell.restart_shell() shell.restart_shell()
......
...@@ -47,6 +47,7 @@ def main(del_exitfunc=False): ...@@ -47,6 +47,7 @@ def main(del_exitfunc=False):
global no_exitfunc global no_exitfunc
no_exitfunc = del_exitfunc no_exitfunc = del_exitfunc
port = 8833 port = 8833
#time.sleep(15) # test subprocess not responding
if sys.argv[1:]: if sys.argv[1:]:
port = int(sys.argv[1]) port = int(sys.argv[1])
sys.argv[:] = [""] sys.argv[:] = [""]
...@@ -90,24 +91,38 @@ def main(del_exitfunc=False): ...@@ -90,24 +91,38 @@ def main(del_exitfunc=False):
continue continue
def manage_socket(address): def manage_socket(address):
for i in range(6): for i in range(3):
time.sleep(i) time.sleep(i)
try: try:
server = MyRPCServer(address, MyHandler) server = MyRPCServer(address, MyHandler)
break break
except socket.error, err: except socket.error, err:
if i < 3: print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
print>>sys.__stderr__, ".. ", + err[1] + ", retrying...."
else:
print>>sys.__stderr__,"\nPython subprocess socket error: "\
+ err[1] + ", retrying...."
else: else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting." print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
"IDLE GUI failed, exiting."
show_socket_error(err, address)
global exit_now global exit_now
exit_now = True exit_now = True
return return
server.handle_request() # A single request only server.handle_request() # A single request only
def show_socket_error(err, address):
import Tkinter
import tkMessageBox
root = Tkinter.Tk()
root.withdraw()
if err[0] == 61: # connection refused
msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
"to your personal firewall configuration. It is safe to "\
"allow this internal connection because no data is visible on "\
"external ports." % address
tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
else:
tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1])
root.destroy()
def print_exception(): def print_exception():
import linecache import linecache
linecache.checkcache() linecache.checkcache()
...@@ -116,7 +131,7 @@ def print_exception(): ...@@ -116,7 +131,7 @@ def print_exception():
typ, val, tb = excinfo = sys.exc_info() typ, val, tb = excinfo = sys.exc_info()
sys.last_type, sys.last_value, sys.last_traceback = excinfo sys.last_type, sys.last_value, sys.last_traceback = excinfo
tbe = traceback.extract_tb(tb) tbe = traceback.extract_tb(tb)
print >>efile, '\nTraceback (most recent call last):' print>>efile, '\nTraceback (most recent call last):'
exclude = ("run.py", "rpc.py", "threading.py", "Queue.py", exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
"RemoteDebugger.py", "bdb.py") "RemoteDebugger.py", "bdb.py")
cleanup_traceback(tbe, exclude) cleanup_traceback(tbe, exclude)
......
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