diff options
Diffstat (limited to 'Lib/idlelib/PyShell.py')
-rw-r--r-- | Lib/idlelib/PyShell.py | 124 |
1 files changed, 72 insertions, 52 deletions
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 6524dff..96efcec 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -36,7 +36,8 @@ from idlelib import RemoteDebugger from idlelib import macosxSupport IDENTCHARS = string.ascii_letters + string.digits + "_" -LOCALHOST = '127.0.0.1' +HOST = '127.0.0.1' # python execution server on localhost loopback +PORT = 0 # someday pass in host, port for remote debug capability try: from signal import SIGTERM @@ -56,20 +57,21 @@ except ImportError: else: def idle_showwarning(message, category, filename, lineno, file=None, line=None): - file = warning_stream + if file is None: + file = warning_stream try: - file.write(warnings.formatwarning(message, category, filename,\ - lineno, file=file, line=line)) + file.write(warnings.formatwarning(message, category, filename, + lineno, line=line)) except IOError: pass ## file (probably __stderr__) is invalid, warning dropped. warnings.showwarning = idle_showwarning - def idle_formatwarning(message, category, filename, lineno, - file=None, line=None): + def idle_formatwarning(message, category, filename, lineno, line=None): """Format warnings the IDLE way""" s = "\nWarning (from warnings module):\n" s += ' File \"%s\", line %s\n' % (filename, lineno) - line = linecache.getline(filename, lineno).strip() \ - if line is None else line + if line is None: + line = linecache.getline(filename, lineno) + line = line.strip() if line: s += " %s\n" % line s += "%s: %s\n>>> " % (category.__name__, message) @@ -82,18 +84,17 @@ def extended_linecache_checkcache(filename=None, Rather than repeating the linecache code, patch it to save the <pyshell#...> entries, call the original linecache.checkcache() - (which destroys them), and then restore the saved entries. + (skipping them), and then restore the saved entries. orig_checkcache is bound at definition time to the original method, allowing it to be patched. - """ cache = linecache.cache save = {} - for filename in cache.keys(): - if filename[:1] + filename[-1:] == '<>': - save[filename] = cache[filename] - orig_checkcache() + for key in list(cache): + if key[:1] + key[-1:] == '<>': + save[key] = cache.pop(key) + orig_checkcache(filename) cache.update(save) # Patch linecache.checkcache(): @@ -206,18 +207,26 @@ class PyShellEditorWindow(EditorWindow): breaks = self.breakpoints filename = self.io.filename try: - lines = open(self.breakpointPath,"r").readlines() + with open(self.breakpointPath,"r") as old_file: + lines = old_file.readlines() except IOError: lines = [] - new_file = open(self.breakpointPath,"w") - for line in lines: - if not line.startswith(filename + '='): - new_file.write(line) - self.update_breakpoints() - breaks = self.breakpoints - if breaks: - new_file.write(filename + '=' + str(breaks) + '\n') - new_file.close() + try: + with open(self.breakpointPath,"w") as new_file: + for line in lines: + if not line.startswith(filename + '='): + new_file.write(line) + self.update_breakpoints() + breaks = self.breakpoints + if breaks: + new_file.write(filename + '=' + str(breaks) + '\n') + except IOError as err: + if not getattr(self.root, "breakpoint_error_displayed", False): + self.root.breakpoint_error_displayed = True + tkMessageBox.showerror(title='IDLE Error', + message='Unable to update breakpoint list:\n%s' + % str(err), + parent=self.text) def restore_file_breaks(self): self.text.update() # this enables setting "BREAK" tags to be visible @@ -341,17 +350,22 @@ class ModifiedInterpreter(InteractiveInterpreter): InteractiveInterpreter.__init__(self, locals=locals) self.save_warnings_filters = None self.restarting = False - self.subprocess_arglist = self.build_subprocess_arglist() + self.subprocess_arglist = None + self.port = PORT + self.original_compiler_flags = self.compile.compiler.flags - port = 8833 rpcclt = None rpcpid = None def spawn_subprocess(self): + if self.subprocess_arglist is None: + self.subprocess_arglist = self.build_subprocess_arglist() args = self.subprocess_arglist self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args) def build_subprocess_arglist(self): + assert (self.port!=0), ( + "Socket should have been assigned a port number.") w = ['-W' + s for s in sys.warnoptions] if 1/2 > 0: # account for new division w.append('-Qnew') @@ -372,11 +386,8 @@ class ModifiedInterpreter(InteractiveInterpreter): return [decorated_exec] + w + ["-c", command, str(self.port)] 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 + addr = (HOST, self.port) + # GUI makes several attempts to acquire socket, listens for connection for i in range(3): time.sleep(i) try: @@ -387,6 +398,18 @@ class ModifiedInterpreter(InteractiveInterpreter): else: self.display_port_binding_error() return None + # if PORT was 0, system will assign an 'ephemeral' port. Find it out: + self.port = self.rpcclt.listening_sock.getsockname()[1] + # if PORT was not 0, probably working with a remote execution server + if PORT != 0: + # To allow reconnection within the 2MSL wait (cf. Stevens TCP + # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic + # on Windows since the implementation allows two active sockets on + # the same address! + self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + self.spawn_subprocess() + #time.sleep(20) # test to simulate GUI not accepting connection # Accept the connection from the Python execution server self.rpcclt.listening_sock.settimeout(10) try: @@ -445,6 +468,7 @@ class ModifiedInterpreter(InteractiveInterpreter): gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt) # reload remote debugger breakpoints for all PyShellEditWindows debug.load_breakpoints() + self.compile.compiler.flags = self.original_compiler_flags self.restarting = False return self.rpcclt @@ -753,13 +777,12 @@ class ModifiedInterpreter(InteractiveInterpreter): 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.", + "IDLE can't bind to a TCP/IP port, which is necessary to " + "communicate with its Python execution server. This might be " + "because no networking is installed on this computer. " + "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): @@ -972,15 +995,6 @@ class PyShell(OutputWindow): COPYRIGHT = \ 'Type "copyright", "credits" or "license()" for more information.' - firewallmessage = """ - **************************************************************** - Personal firewall software may warn about the connection IDLE - makes to its subprocess using this computer's internal loopback - interface. This connection is not visible on any external - interface and no data is sent to or received from the Internet. - **************************************************************** - """ - def begin(self): self.resetoutput() if use_subprocess: @@ -991,9 +1005,8 @@ class PyShell(OutputWindow): return False else: nosub = "==== No Subprocess ====" - self.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" % - (sys.version, sys.platform, self.COPYRIGHT, - self.firewallmessage, idlever.IDLE_VERSION, nosub)) + self.write("Python %s on %s\n%s\n%s" % + (sys.version, sys.platform, self.COPYRIGHT, nosub)) self.showprompt() import Tkinter Tkinter._default_root = None # 03Jan04 KBK What's this? @@ -1310,7 +1323,7 @@ def main(): global flist, root, use_subprocess use_subprocess = True - enable_shell = False + enable_shell = True enable_edit = False debug = False cmd = None @@ -1331,6 +1344,7 @@ def main(): enable_shell = True if o == '-e': enable_edit = True + enable_shell = False if o == '-h': sys.stdout.write(usage_msg) sys.exit() @@ -1381,7 +1395,6 @@ def main(): edit_start = idleConf.GetOption('main', 'General', 'editor-on-startup', type='bool') enable_edit = enable_edit or edit_start - enable_shell = enable_shell or not edit_start # start editor and/or shell windows: root = Tk(className="Idle") @@ -1429,6 +1442,13 @@ def main(): shell.interp.prepend_syspath(script) shell.interp.execfile(script) + # Check for problematic OS X Tk versions and print a warning message + # in the IDLE shell window; this is less intrusive than always opening + # a separate window. + tkversionwarning = macosxSupport.tkVersionWarning(root) + if tkversionwarning: + shell.interp.runcommand(''.join(("print('", tkversionwarning, "')"))) + root.mainloop() root.destroy() |