blob: 83433b1cf0a4591bc3fc9c9833987b9aa17f507c [file] [log] [blame]
wohlganger58fc71c2017-09-10 16:19:47 -05001"""Execute code from an editor.
David Scherer7aced172000-08-15 01:13:23 +00002
wohlganger58fc71c2017-09-10 16:19:47 -05003Check module: do a full syntax check of the current module.
4Also run the tabnanny to catch any inconsistent tabs.
David Scherer7aced172000-08-15 01:13:23 +00005
wohlganger58fc71c2017-09-10 16:19:47 -05006Run module: also execute the module's code in the __main__ namespace.
7The window must have been saved previously. The module is added to
8sys.modules, and is also added to the __main__ namespace.
David Scherer7aced172000-08-15 01:13:23 +00009
wohlganger58fc71c2017-09-10 16:19:47 -050010TODO: Specify command line arguments in a dialog box.
David Scherer7aced172000-08-15 01:13:23 +000011"""
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +000012import os
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000013import tabnanny
14import tokenize
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040015
Georg Brandl14fc4272008-05-17 18:39:55 +000016import tkinter.messagebox as tkMessageBox
David Scherer7aced172000-08-15 01:13:23 +000017
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040018from idlelib.config import idleConf
19from idlelib import macosx
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040020from idlelib import pyshell
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +000021
David Scherer7aced172000-08-15 01:13:23 +000022indent_message = """Error: Inconsistent indentation detected!
23
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000241) Your indentation is outright incorrect (easy to fix), OR
David Scherer7aced172000-08-15 01:13:23 +000025
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000262) Your indentation mixes tabs and spaces.
David Scherer7aced172000-08-15 01:13:23 +000027
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +000028To fix case 2, change all tabs to spaces by using Edit->Select All followed \
29by Format->Untabify Region and specify the number of columns used by each tab.
30"""
Kurt B. Kaiser969de452002-06-12 03:28:57 +000031
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -040032
David Scherer7aced172000-08-15 01:13:23 +000033class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000034
David Scherer7aced172000-08-15 01:13:23 +000035 def __init__(self, editwin):
36 self.editwin = editwin
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040037 # Provide instance variables referenced by debugger
David Scherer7aced172000-08-15 01:13:23 +000038 # XXX This should be done differently
39 self.flist = self.editwin.flist
Thomas Wouters0e3f5912006-08-11 14:57:12 +000040 self.root = self.editwin.root
David Scherer7aced172000-08-15 01:13:23 +000041
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040042 if macosx.isCocoaTk():
Ronald Oussoren5ee05672011-05-17 14:48:40 +020043 self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
44
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +000045 def check_module_event(self, event):
David Scherer7aced172000-08-15 01:13:23 +000046 filename = self.getfilename()
47 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +000048 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +000049 if not self.checksyntax(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000050 return 'break'
David Scherer7aced172000-08-15 01:13:23 +000051 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000052 return 'break'
Serhiy Storchaka213ce122017-06-27 07:02:32 +030053 return "break"
David Scherer7aced172000-08-15 01:13:23 +000054
55 def tabnanny(self, filename):
Martin v. Löwis975a0792009-01-18 20:15:42 +000056 # XXX: tabnanny should work on binary files as well
Victor Stinner85c67722011-09-02 00:57:04 +020057 with tokenize.open(filename) as f:
58 try:
59 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
60 except tokenize.TokenError as msg:
Terry Jan Reedyc6dd5b12015-08-14 16:59:42 -040061 msgtxt, (lineno, start) = msg.args
Victor Stinner85c67722011-09-02 00:57:04 +020062 self.editwin.gotoline(lineno)
63 self.errorbox("Tabnanny Tokenizing Error",
64 "Token Error: %s" % msgtxt)
65 return False
66 except tabnanny.NannyNag as nag:
67 # The error messages from tabnanny are too confusing...
68 self.editwin.gotoline(nag.get_lineno())
69 self.errorbox("Tab/space error", indent_message)
70 return False
Neal Norwitz6453c1f2002-11-30 19:18:46 +000071 return True
David Scherer7aced172000-08-15 01:13:23 +000072
73 def checksyntax(self, filename):
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000074 self.shell = shell = self.flist.open_shell()
75 saved_stream = shell.get_warning_stream()
76 shell.set_warning_stream(shell.stderr)
Terry Jan Reedy95f34ab2013-08-04 15:39:03 -040077 with open(filename, 'rb') as f:
78 source = f.read()
Martin v. Löwis975a0792009-01-18 20:15:42 +000079 if b'\r' in source:
80 source = source.replace(b'\r\n', b'\n')
81 source = source.replace(b'\r', b'\n')
82 if source and source[-1] != ord(b'\n'):
83 source = source + b'\n'
Guido van Rossum33d26892007-08-05 15:29:28 +000084 editwin = self.editwin
85 text = editwin.text
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +000086 text.tag_remove("ERROR", "1.0", "end")
David Scherer7aced172000-08-15 01:13:23 +000087 try:
Guido van Rossum33d26892007-08-05 15:29:28 +000088 # If successful, return the compiled code
89 return compile(source, filename, "exec")
Ned Deily79746422011-09-14 14:49:14 -070090 except (SyntaxError, OverflowError, ValueError) as value:
91 msg = getattr(value, 'msg', '') or value or "<no detail available>"
92 lineno = getattr(value, 'lineno', '') or 1
93 offset = getattr(value, 'offset', '') or 0
Guido van Rossum33d26892007-08-05 15:29:28 +000094 if offset == 0:
95 lineno += 1 #mark end of offending line
96 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
97 editwin.colorize_syntax_error(text, pos)
98 self.errorbox("SyntaxError", "%-20s" % msg)
99 return False
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000100 finally:
101 shell.set_warning_stream(saved_stream)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000102
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +0000103 def run_module_event(self, event):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400104 if macosx.isCocoaTk():
Ronald Oussoren5ee05672011-05-17 14:48:40 +0200105 # Tk-Cocoa in MacOSX is broken until at least
106 # Tk 8.5.9, and without this rather
107 # crude workaround IDLE would hang when a user
108 # tries to run a module using the keyboard shortcut
109 # (the menu item works fine).
110 self.editwin.text_frame.after(200,
111 lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>'))
112 return 'break'
113 else:
114 return self._run_module_event(event)
115
116 def _run_module_event(self, event):
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000117 """Run the module after setting up the environment.
118
119 First check the syntax. If OK, make sure the shell is active and
120 then transfer the arguments, set the run environment's working
121 directory to the directory of the module being executed and also
122 add that directory to its sys.path if not already included.
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000123 """
Ronald Oussoren5ee05672011-05-17 14:48:40 +0200124
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000125 filename = self.getfilename()
126 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +0000127 return 'break'
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000128 code = self.checksyntax(filename)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000129 if not code:
Christian Heimesa156e092008-02-16 07:38:31 +0000130 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +0000131 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +0000132 return 'break'
Terry Jan Reedyda4c4672012-01-31 02:26:32 -0500133 interp = self.shell.interp
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400134 if pyshell.use_subprocess:
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -0400135 interp.restart_subprocess(with_cwd=False, filename=
136 self.editwin._filename_to_unicode(filename))
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000137 dirname = os.path.dirname(filename)
Chui Tey5d2af632002-05-26 13:36:41 +0000138 # XXX Too often this discards arguments the user just set...
139 interp.runcommand("""if 1:
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300140 __file__ = {filename!r}
Chui Tey5d2af632002-05-26 13:36:41 +0000141 import sys as _sys
142 from os.path import basename as _basename
143 if (not _sys.argv or
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300144 _basename(_sys.argv[0]) != _basename(__file__)):
145 _sys.argv = [__file__]
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000146 import os as _os
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300147 _os.chdir({dirname!r})
148 del _sys, _basename, _os
149 \n""".format(filename=filename, dirname=dirname))
Kurt B. Kaiser11659ad2003-05-15 23:23:21 +0000150 interp.prepend_syspath(filename)
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000151 # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
152 # go to __stderr__. With subprocess, they go to the shell.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400153 # Need to change streams in pyshell.ModifiedInterpreter.
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000154 interp.runcode(code)
Christian Heimesa156e092008-02-16 07:38:31 +0000155 return 'break'
David Scherer7aced172000-08-15 01:13:23 +0000156
157 def getfilename(self):
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000158 """Get source filename. If not saved, offer to save (or create) file
159
160 The debugger requires a source file. Make sure there is one, and that
161 the current version of the source buffer has been saved. If the user
162 declines to save or cancels the Save As dialog, return None.
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000163
164 If the user has configured IDLE for Autosave, the file will be
165 silently saved if it already exists and is dirty.
Kurt B. Kaiser8d1f11b2003-05-26 22:20:34 +0000166
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000167 """
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000168 filename = self.editwin.io.filename
David Scherer7aced172000-08-15 01:13:23 +0000169 if not self.editwin.get_saved():
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000170 autosave = idleConf.GetOption('main', 'General',
171 'autosave', type='bool')
172 if autosave and filename:
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000173 self.editwin.io.save(None)
174 else:
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400175 confirm = self.ask_save_dialog()
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000176 self.editwin.text.focus_set()
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400177 if confirm:
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000178 self.editwin.io.save(None)
179 filename = self.editwin.io.filename
180 else:
181 filename = None
David Scherer7aced172000-08-15 01:13:23 +0000182 return filename
183
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000184 def ask_save_dialog(self):
185 msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400186 confirm = tkMessageBox.askokcancel(title="Save Before Run or Check",
187 message=msg,
188 default=tkMessageBox.OK,
Terry Jan Reedy3be2e542015-09-25 22:22:55 -0400189 parent=self.editwin.text)
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400190 return confirm
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000191
David Scherer7aced172000-08-15 01:13:23 +0000192 def errorbox(self, title, message):
193 # XXX This should really be a function of EditorWindow...
Terry Jan Reedy3be2e542015-09-25 22:22:55 -0400194 tkMessageBox.showerror(title, message, parent=self.editwin.text)
David Scherer7aced172000-08-15 01:13:23 +0000195 self.editwin.text.focus_set()
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400196
197
198if __name__ == "__main__":
199 from unittest import main
200 main('idlelib.idle_test.test_runscript', verbosity=2,)