mirror of
https://github.com/python/cpython.git
synced 2024-11-26 11:24:40 +08:00
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.
This commit is contained in:
parent
1fe9750200
commit
af3eb87802
@ -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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
# 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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
"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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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():
|
|||||||
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():
|
|||||||
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():
|
|||||||
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:
|
|||||||
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):
|
|||||||
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):
|
|||||||
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():
|
|||||||
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user