diff options
Diffstat (limited to 'Lib/idlelib/PyShell.py')
| -rw-r--r-- | Lib/idlelib/PyShell.py | 144 | 
1 files changed, 80 insertions, 64 deletions
| diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 0fa3d765aa..6bf0a8c65d 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -1,13 +1,15 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 +import getopt  import os  import os.path -import sys -import getopt  import re  import socket -import time +import subprocess +import sys  import threading +import time +import tokenize  import traceback  import types @@ -37,11 +39,6 @@ from idlelib import macosxSupport  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 -except ImportError: -    SIGTERM = 15 -  # Override warnings module to write to warning_stream.  Initialize to send IDLE  # internal warnings to the console.  ScriptBinding.check_syntax() will  # temporarily redirect the stream to the shell window to display warnings when @@ -55,20 +52,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) @@ -81,18 +79,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: -        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(): @@ -205,18 +202,26 @@ class PyShellEditorWindow(EditorWindow):          breaks = self.breakpoints          filename = self.io.filename          try: -            lines = open(self.breakpointPath,"r").readlines() +            with open(self.breakpointPath, "r") as fp: +                lines = fp.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 @@ -224,7 +229,8 @@ class PyShellEditorWindow(EditorWindow):          if filename is None:              return          if os.path.isfile(self.breakpointPath): -            lines = open(self.breakpointPath,"r").readlines() +            with open(self.breakpointPath, "r") as fp: +                lines = fp.readlines()              for line in lines:                  if line.startswith(filename + '='):                      breakpoint_linenumbers = eval(line[len(filename)+1:]) @@ -342,15 +348,15 @@ class ModifiedInterpreter(InteractiveInterpreter):          self.restarting = False          self.subprocess_arglist = None          self.port = PORT +        self.original_compiler_flags = self.compile.compiler.flags      rpcclt = None -    rpcpid = None +    rpcsubproc = 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) +        self.rpcsubproc = subprocess.Popen(self.subprocess_arglist)      def build_subprocess_arglist(self):          assert (self.port!=0), ( @@ -365,12 +371,7 @@ class ModifiedInterpreter(InteractiveInterpreter):              command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)          else:              command = "__import__('run').main(%r)" % (del_exitf,) -        if sys.platform[:3] == 'win' and ' ' in sys.executable: -            # handle embedded space in path by quoting the argument -            decorated_exec = '"%s"' % sys.executable -        else: -            decorated_exec = sys.executable -        return [decorated_exec] + w + ["-c", command, str(self.port)] +        return [sys.executable] + w + ["-c", command, str(self.port)]      def start_subprocess(self):          addr = (HOST, self.port) @@ -410,11 +411,11 @@ class ModifiedInterpreter(InteractiveInterpreter):          self.rpcclt.register("flist", self.tkconsole.flist)          self.rpcclt.register("linecache", linecache)          self.rpcclt.register("interp", self) -        self.transfer_path() +        self.transfer_path(with_cwd=True)          self.poll_subprocess()          return self.rpcclt -    def restart_subprocess(self): +    def restart_subprocess(self, with_cwd=False):          if self.restarting:              return self.rpcclt          self.restarting = True @@ -428,7 +429,7 @@ class ModifiedInterpreter(InteractiveInterpreter):                  pass          # Kill subprocess, spawn a new one, accept connection.          self.rpcclt.close() -        self.unix_terminate() +        self.terminate_subprocess()          console = self.tkconsole          was_executing = console.executing          console.executing = False @@ -438,7 +439,7 @@ class ModifiedInterpreter(InteractiveInterpreter):          except socket.timeout as err:              self.display_no_subprocess_error()              return None -        self.transfer_path() +        self.transfer_path(with_cwd=with_cwd)          # annotate restart in shell window and mark it          console.text.delete("iomark", "end-1c")          if was_executing: @@ -455,6 +456,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 @@ -469,30 +471,35 @@ class ModifiedInterpreter(InteractiveInterpreter):              self.rpcclt.close()          except AttributeError:  # no socket              pass -        self.unix_terminate() +        self.terminate_subprocess()          self.tkconsole.executing = False          self.rpcclt = None -    def unix_terminate(self): -        "UNIX: make sure subprocess is terminated and collect status" -        if hasattr(os, 'kill'): +    def terminate_subprocess(self): +        "Make sure subprocess is terminated" +        try: +            self.rpcsubproc.kill() +        except OSError: +            # process already terminated +            return +        else:              try: -                os.kill(self.rpcpid, SIGTERM) +                self.rpcsubproc.wait()              except OSError: -                # process already terminated:                  return -            else: -                try: -                    os.waitpid(self.rpcpid, 0) -                except OSError: -                    return -    def transfer_path(self): +    def transfer_path(self, with_cwd=False): +        if with_cwd:        # Issue 13506 +            path = ['']     # include Current Working Directory +            path.extend(sys.path) +        else: +            path = sys.path +          self.runcommand("""if 1:          import sys as _sys          _sys.path = %r          del _sys -        \n""" % (sys.path,)) +        \n""" % (path,))      active_seq = None @@ -582,7 +589,8 @@ class ModifiedInterpreter(InteractiveInterpreter):      def execfile(self, filename, source=None):          "Execute an existing file"          if source is None: -            source = open(filename, "r").read() +            with tokenize.open(filename) as fp: +                source = fp.read()          try:              code = compile(source, filename, "exec")          except (OverflowError, SyntaxError): @@ -651,9 +659,9 @@ class ModifiedInterpreter(InteractiveInterpreter):          text = tkconsole.text          text.tag_remove("ERROR", "1.0", "end")          type, value, tb = sys.exc_info() -        msg = value.msg or "<no detail available>" -        lineno = value.lineno or 1 -        offset = value.offset or 0 +        msg = getattr(value, 'msg', '') or value or "<no detail available>" +        lineno = getattr(value, 'lineno', '') or 1 +        offset = getattr(value, 'offset', '') or 0          if offset == 0:              lineno += 1 #mark end of offending line          if lineno == 1: @@ -1185,7 +1193,8 @@ class PyShell(OutputWindow):          self.text.see("restart")      def restart_shell(self, event=None): -        self.interp.restart_subprocess() +        "Callback for Run/Restart Shell Cntl-F6" +        self.interp.restart_subprocess(with_cwd=True)      def showprompt(self):          self.resetoutput() @@ -1417,6 +1426,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() | 
