(root)/
Python-3.11.7/
Lib/
idlelib/
runscript.py
       1  """Execute code from an editor.
       2  
       3  Check module: do a full syntax check of the current module.
       4  Also run the tabnanny to catch any inconsistent tabs.
       5  
       6  Run module: also execute the module's code in the __main__ namespace.
       7  The window must have been saved previously. The module is added to
       8  sys.modules, and is also added to the __main__ namespace.
       9  
      10  TODO: Specify command line arguments in a dialog box.
      11  """
      12  import os
      13  import tabnanny
      14  import time
      15  import tokenize
      16  
      17  from tkinter import messagebox
      18  
      19  from idlelib.config import idleConf
      20  from idlelib import macosx
      21  from idlelib import pyshell
      22  from idlelib.query import CustomRun
      23  from idlelib import outwin
      24  
      25  indent_message = """Error: Inconsistent indentation detected!
      26  
      27  1) Your indentation is outright incorrect (easy to fix), OR
      28  
      29  2) Your indentation mixes tabs and spaces.
      30  
      31  To fix case 2, change all tabs to spaces by using Edit->Select All followed \
      32  by Format->Untabify Region and specify the number of columns used by each tab.
      33  """
      34  
      35  
      36  class ESC[4;38;5;81mScriptBinding:
      37  
      38      def __init__(self, editwin):
      39          self.editwin = editwin
      40          # Provide instance variables referenced by debugger
      41          # XXX This should be done differently
      42          self.flist = self.editwin.flist
      43          self.root = self.editwin.root
      44          # cli_args is list of strings that extends sys.argv
      45          self.cli_args = []
      46          self.perf = 0.0    # Workaround for macOS 11 Uni2; see bpo-42508.
      47  
      48      def check_module_event(self, event):
      49          if isinstance(self.editwin, outwin.OutputWindow):
      50              self.editwin.text.bell()
      51              return 'break'
      52          filename = self.getfilename()
      53          if not filename:
      54              return 'break'
      55          if not self.checksyntax(filename):
      56              return 'break'
      57          if not self.tabnanny(filename):
      58              return 'break'
      59          return "break"
      60  
      61      def tabnanny(self, filename):
      62          # XXX: tabnanny should work on binary files as well
      63          with tokenize.open(filename) as f:
      64              try:
      65                  tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
      66              except tokenize.TokenError as msg:
      67                  msgtxt, (lineno, start) = msg.args
      68                  self.editwin.gotoline(lineno)
      69                  self.errorbox("Tabnanny Tokenizing Error",
      70                                "Token Error: %s" % msgtxt)
      71                  return False
      72              except tabnanny.NannyNag as nag:
      73                  # The error messages from tabnanny are too confusing...
      74                  self.editwin.gotoline(nag.get_lineno())
      75                  self.errorbox("Tab/space error", indent_message)
      76                  return False
      77          return True
      78  
      79      def checksyntax(self, filename):
      80          self.shell = shell = self.flist.open_shell()
      81          saved_stream = shell.get_warning_stream()
      82          shell.set_warning_stream(shell.stderr)
      83          with open(filename, 'rb') as f:
      84              source = f.read()
      85          if b'\r' in source:
      86              source = source.replace(b'\r\n', b'\n')
      87              source = source.replace(b'\r', b'\n')
      88          if source and source[-1] != ord(b'\n'):
      89              source = source + b'\n'
      90          editwin = self.editwin
      91          text = editwin.text
      92          text.tag_remove("ERROR", "1.0", "end")
      93          try:
      94              # If successful, return the compiled code
      95              return compile(source, filename, "exec")
      96          except (SyntaxError, OverflowError, ValueError) as value:
      97              msg = getattr(value, 'msg', '') or value or "<no detail available>"
      98              lineno = getattr(value, 'lineno', '') or 1
      99              offset = getattr(value, 'offset', '') or 0
     100              if offset == 0:
     101                  lineno += 1  #mark end of offending line
     102              pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
     103              editwin.colorize_syntax_error(text, pos)
     104              self.errorbox("SyntaxError", "%-20s" % msg)
     105              return False
     106          finally:
     107              shell.set_warning_stream(saved_stream)
     108  
     109      def run_custom_event(self, event):
     110          return self.run_module_event(event, customize=True)
     111  
     112      def run_module_event(self, event, *, customize=False):
     113          """Run the module after setting up the environment.
     114  
     115          First check the syntax.  Next get customization.  If OK, make
     116          sure the shell is active and then transfer the arguments, set
     117          the run environment's working directory to the directory of the
     118          module being executed and also add that directory to its
     119          sys.path if not already included.
     120          """
     121          if macosx.isCocoaTk() and (time.perf_counter() - self.perf < .05):
     122              return 'break'
     123          if isinstance(self.editwin, outwin.OutputWindow):
     124              self.editwin.text.bell()
     125              return 'break'
     126          filename = self.getfilename()
     127          if not filename:
     128              return 'break'
     129          code = self.checksyntax(filename)
     130          if not code:
     131              return 'break'
     132          if not self.tabnanny(filename):
     133              return 'break'
     134          if customize:
     135              title = f"Customize {self.editwin.short_title()} Run"
     136              run_args = CustomRun(self.shell.text, title,
     137                                   cli_args=self.cli_args).result
     138              if not run_args:  # User cancelled.
     139                  return 'break'
     140          self.cli_args, restart = run_args if customize else ([], True)
     141          interp = self.shell.interp
     142          if pyshell.use_subprocess and restart:
     143              interp.restart_subprocess(
     144                      with_cwd=False, filename=filename)
     145          dirname = os.path.dirname(filename)
     146          argv = [filename]
     147          if self.cli_args:
     148              argv += self.cli_args
     149          interp.runcommand(f"""if 1:
     150              __file__ = {filename!r}
     151              import sys as _sys
     152              from os.path import basename as _basename
     153              argv = {argv!r}
     154              if (not _sys.argv or
     155                  _basename(_sys.argv[0]) != _basename(__file__) or
     156                  len(argv) > 1):
     157                  _sys.argv = argv
     158              import os as _os
     159              _os.chdir({dirname!r})
     160              del _sys, argv, _basename, _os
     161              \n""")
     162          interp.prepend_syspath(filename)
     163          # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
     164          #         go to __stderr__.  With subprocess, they go to the shell.
     165          #         Need to change streams in pyshell.ModifiedInterpreter.
     166          interp.runcode(code)
     167          return 'break'
     168  
     169      def getfilename(self):
     170          """Get source filename.  If not saved, offer to save (or create) file
     171  
     172          The debugger requires a source file.  Make sure there is one, and that
     173          the current version of the source buffer has been saved.  If the user
     174          declines to save or cancels the Save As dialog, return None.
     175  
     176          If the user has configured IDLE for Autosave, the file will be
     177          silently saved if it already exists and is dirty.
     178  
     179          """
     180          filename = self.editwin.io.filename
     181          if not self.editwin.get_saved():
     182              autosave = idleConf.GetOption('main', 'General',
     183                                            'autosave', type='bool')
     184              if autosave and filename:
     185                  self.editwin.io.save(None)
     186              else:
     187                  confirm = self.ask_save_dialog()
     188                  self.editwin.text.focus_set()
     189                  if confirm:
     190                      self.editwin.io.save(None)
     191                      filename = self.editwin.io.filename
     192                  else:
     193                      filename = None
     194          return filename
     195  
     196      def ask_save_dialog(self):
     197          msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
     198          confirm = messagebox.askokcancel(title="Save Before Run or Check",
     199                                             message=msg,
     200                                             default=messagebox.OK,
     201                                             parent=self.editwin.text)
     202          return confirm
     203  
     204      def errorbox(self, title, message):
     205          # XXX This should really be a function of EditorWindow...
     206          messagebox.showerror(title, message, parent=self.editwin.text)
     207          self.editwin.text.focus_set()
     208          self.perf = time.perf_counter()
     209  
     210  
     211  if __name__ == "__main__":
     212      from unittest import main
     213      main('idlelib.idle_test.test_runscript', verbosity=2,)