Index: PyShell.py =================================================================== --- PyShell.py (revision 50822) +++ PyShell.py (working copy) @@ -7,6 +7,7 @@ import getopt import re import socket +import select import time import threading import traceback @@ -339,14 +340,13 @@ InteractiveInterpreter.__init__(self, locals=locals) self.save_warnings_filters = None self.restarting = False - self.subprocess_arglist = self.build_subprocess_arglist() - port = 8833 + port = None rpcclt = None rpcpid = None def spawn_subprocess(self): - args = self.subprocess_arglist + args = self.build_subprocess_arglist() self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args) def build_subprocess_arglist(self): @@ -367,29 +367,57 @@ decorated_exec = sys.executable return [decorated_exec] + w + ["-c", command, str(self.port)] + timeout = 10 # seconds + max_timeouts = 2 + max_ports_to_try = 10 + + ports = range(49152, 65536) # 'dynamic' ports as described by the DCCP + # http://www.iana.org/assignments/port-numbers + # Bad ports taken from: + # http://www.doshelp.com/Ports/Trojan_Ports.htm + # http://www.simovits.com/trojans/trojans.html + bad_ports = [49301, 49683, 49698, 50000, 50005, 50021, 50130, 50505, 50551, + 50552, 50766, 50776, 50829, 51234, 51435, 51966, 52365, 52901, + 53001, 54283, 54320, 54321, 55165, 55555, 55665, 55666, 56565, + 57163, 57341, 57785, 58008, 58009, 58134, 58339, 58666, 59211, + 60000, 60001, 60006, 60008, 60068, 60411, 60551, 60552, 60666, + 61000, 61115, 61337, 61348, 61440, 61466, 61603, 61746, 61747, + 61748, 61979, 62011, 63485, 63808, 63809, 64101, 64429, 65000, + 65289, 65421, 65422, 65432, 65506, 65530, 65535] + ports = filter(lambda p,bps=bad_ports:p not in bps, ports) + import random + random.shuffle(ports) + ports = ports[:max_ports_to_try] + 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) - # Idle starts listening for connection on localhost - for i in range(3): - time.sleep(i) + timeouts = 0 + for p in self.ports: + # IDLE starts listening for connection on localhost + addr = (LOCALHOST, p) try: self.rpcclt = MyRPCClient(addr) - break except socket.error, err: - pass + continue + self.port = p + self.spawn_subprocess() + + # Accept the connection from the Python execution server + r,w,e = select.select([self.rpcclt.listening_sock], [], [], + self.timeout) + if not r: + self.rpcclt.listening_sock.close() + self.unix_terminate() + timeouts = timeouts + 1 + if timeouts >= self.max_timeouts: + self.display_no_subprocess_error() + return None + continue + self.rpcclt.accept() + break else: self.display_port_binding_error() return None - # Accept the connection from the Python execution server - 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("stdout", self.tkconsole.stdout) self.rpcclt.register("stderr", self.tkconsole.stderr) @@ -419,11 +447,12 @@ was_executing = console.executing console.executing = False self.spawn_subprocess() - try: - self.rpcclt.accept() - except socket.timeout, err: + r,w,e = select.select([self.rpcclt.listening_sock], [], [], + self.timeout) + if not r: self.display_no_subprocess_error() return None + self.rpcclt.accept() self.transfer_path() # annotate restart in shell window and mark it console.text.delete("iomark", "end-1c") @@ -593,7 +622,7 @@ source = source.encode(IOBinding.encoding) except UnicodeError: self.tkconsole.resetoutput() - self.write("Unsupported characters in input") + self.write("Unsupported characters in input\n") return try: # InteractiveInterpreter.runsource() calls its runcode() method, @@ -735,13 +764,13 @@ def display_port_binding_error(self): tkMessageBox.showerror( "Port Binding Error", - "IDLE can't bind TCP/IP port 8833, which is necessary to " + "IDLE can't bind any TCP/IP port, 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.", + "no networking is installed on this computer or IDLE is being " + "blocked from binding a port. Try again, or 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):