blob: d700525a73062d95954936ae7b0b3b32e2718bec [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001"""Extension to execute code outside the Python shell window.
2
Kurt B. Kaiser969de452002-06-12 03:28:57 +00003This adds the following commands:
David Scherer7aced172000-08-15 01:13:23 +00004
Kurt B. Kaiser969de452002-06-12 03:28:57 +00005- Check module does a full syntax check of the current module.
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +00006 It also runs the tabnanny to catch any inconsistent tabs.
David Scherer7aced172000-08-15 01:13:23 +00007
Kurt B. Kaiser63857a42002-09-05 02:31:20 +00008- Run module executes the module's code in the __main__ namespace. The window
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +00009 must have been saved previously. The module is added to sys.modules, and is
10 also added to the __main__ namespace.
David Scherer7aced172000-08-15 01:13:23 +000011
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000012XXX GvR Redesign this interface (yet again) as follows:
Chui Tey5d2af632002-05-26 13:36:41 +000013
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +000014- Present a dialog box for ``Run Module''
Chui Tey5d2af632002-05-26 13:36:41 +000015
16- Allow specify command line arguments in the dialog box
17
David Scherer7aced172000-08-15 01:13:23 +000018"""
19
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +000020import os
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000021import tabnanny
22import tokenize
Georg Brandl14fc4272008-05-17 18:39:55 +000023import tkinter.messagebox as tkMessageBox
Terry Jan Reedy27336182015-05-14 18:10:50 -040024from idlelib import PyShell
David Scherer7aced172000-08-15 01:13:23 +000025
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000026from idlelib.configHandler import idleConf
Ronald Oussoren5ee05672011-05-17 14:48:40 +020027from idlelib import macosxSupport
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +000028
David Scherer7aced172000-08-15 01:13:23 +000029indent_message = """Error: Inconsistent indentation detected!
30
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000311) Your indentation is outright incorrect (easy to fix), OR
David Scherer7aced172000-08-15 01:13:23 +000032
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000332) Your indentation mixes tabs and spaces.
David Scherer7aced172000-08-15 01:13:23 +000034
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +000035To fix case 2, change all tabs to spaces by using Edit->Select All followed \
36by Format->Untabify Region and specify the number of columns used by each tab.
37"""
Kurt B. Kaiser969de452002-06-12 03:28:57 +000038
David Scherer7aced172000-08-15 01:13:23 +000039class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000040
David Scherer7aced172000-08-15 01:13:23 +000041 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000042 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000043 ('Check Module', '<<check-module>>'),
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +000044 ('Run Module', '<<run-module>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000045
46 def __init__(self, editwin):
47 self.editwin = editwin
48 # Provide instance variables referenced by Debugger
49 # XXX This should be done differently
50 self.flist = self.editwin.flist
Thomas Wouters0e3f5912006-08-11 14:57:12 +000051 self.root = self.editwin.root
David Scherer7aced172000-08-15 01:13:23 +000052
Ned Deilyb7601672014-03-27 20:49:14 -070053 if macosxSupport.isCocoaTk():
Ronald Oussoren5ee05672011-05-17 14:48:40 +020054 self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
55
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +000056 def check_module_event(self, event):
David Scherer7aced172000-08-15 01:13:23 +000057 filename = self.getfilename()
58 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +000059 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +000060 if not self.checksyntax(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000061 return 'break'
David Scherer7aced172000-08-15 01:13:23 +000062 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000063 return 'break'
David Scherer7aced172000-08-15 01:13:23 +000064
65 def tabnanny(self, filename):
Martin v. Löwis975a0792009-01-18 20:15:42 +000066 # XXX: tabnanny should work on binary files as well
Victor Stinner85c67722011-09-02 00:57:04 +020067 with tokenize.open(filename) as f:
68 try:
69 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
70 except tokenize.TokenError as msg:
71 msgtxt, (lineno, start) = msg
72 self.editwin.gotoline(lineno)
73 self.errorbox("Tabnanny Tokenizing Error",
74 "Token Error: %s" % msgtxt)
75 return False
76 except tabnanny.NannyNag as nag:
77 # The error messages from tabnanny are too confusing...
78 self.editwin.gotoline(nag.get_lineno())
79 self.errorbox("Tab/space error", indent_message)
80 return False
Neal Norwitz6453c1f2002-11-30 19:18:46 +000081 return True
David Scherer7aced172000-08-15 01:13:23 +000082
83 def checksyntax(self, filename):
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000084 self.shell = shell = self.flist.open_shell()
85 saved_stream = shell.get_warning_stream()
86 shell.set_warning_stream(shell.stderr)
Terry Jan Reedy95f34ab2013-08-04 15:39:03 -040087 with open(filename, 'rb') as f:
88 source = f.read()
Martin v. Löwis975a0792009-01-18 20:15:42 +000089 if b'\r' in source:
90 source = source.replace(b'\r\n', b'\n')
91 source = source.replace(b'\r', b'\n')
92 if source and source[-1] != ord(b'\n'):
93 source = source + b'\n'
Guido van Rossum33d26892007-08-05 15:29:28 +000094 editwin = self.editwin
95 text = editwin.text
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +000096 text.tag_remove("ERROR", "1.0", "end")
David Scherer7aced172000-08-15 01:13:23 +000097 try:
Guido van Rossum33d26892007-08-05 15:29:28 +000098 # If successful, return the compiled code
99 return compile(source, filename, "exec")
Ned Deily79746422011-09-14 14:49:14 -0700100 except (SyntaxError, OverflowError, ValueError) as value:
101 msg = getattr(value, 'msg', '') or value or "<no detail available>"
102 lineno = getattr(value, 'lineno', '') or 1
103 offset = getattr(value, 'offset', '') or 0
Guido van Rossum33d26892007-08-05 15:29:28 +0000104 if offset == 0:
105 lineno += 1 #mark end of offending line
106 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
107 editwin.colorize_syntax_error(text, pos)
108 self.errorbox("SyntaxError", "%-20s" % msg)
109 return False
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000110 finally:
111 shell.set_warning_stream(saved_stream)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000112
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +0000113 def run_module_event(self, event):
Ned Deilyb7601672014-03-27 20:49:14 -0700114 if macosxSupport.isCocoaTk():
Ronald Oussoren5ee05672011-05-17 14:48:40 +0200115 # Tk-Cocoa in MacOSX is broken until at least
116 # Tk 8.5.9, and without this rather
117 # crude workaround IDLE would hang when a user
118 # tries to run a module using the keyboard shortcut
119 # (the menu item works fine).
120 self.editwin.text_frame.after(200,
121 lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>'))
122 return 'break'
123 else:
124 return self._run_module_event(event)
125
126 def _run_module_event(self, event):
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000127 """Run the module after setting up the environment.
128
129 First check the syntax. If OK, make sure the shell is active and
130 then transfer the arguments, set the run environment's working
131 directory to the directory of the module being executed and also
132 add that directory to its sys.path if not already included.
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000133 """
Ronald Oussoren5ee05672011-05-17 14:48:40 +0200134
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000135 filename = self.getfilename()
136 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +0000137 return 'break'
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000138 code = self.checksyntax(filename)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000139 if not code:
Christian Heimesa156e092008-02-16 07:38:31 +0000140 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +0000141 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +0000142 return 'break'
Terry Jan Reedyda4c4672012-01-31 02:26:32 -0500143 interp = self.shell.interp
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +0000144 if PyShell.use_subprocess:
Terry Jan Reedy231007f2015-07-31 22:34:37 -0400145 interp.restart_subprocess(with_cwd=False, filename=code.co_filename)
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000146 dirname = os.path.dirname(filename)
Chui Tey5d2af632002-05-26 13:36:41 +0000147 # XXX Too often this discards arguments the user just set...
148 interp.runcommand("""if 1:
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300149 __file__ = {filename!r}
Chui Tey5d2af632002-05-26 13:36:41 +0000150 import sys as _sys
151 from os.path import basename as _basename
152 if (not _sys.argv or
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300153 _basename(_sys.argv[0]) != _basename(__file__)):
154 _sys.argv = [__file__]
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000155 import os as _os
Andrew Svetlovdfe980b2012-04-05 21:54:39 +0300156 _os.chdir({dirname!r})
157 del _sys, _basename, _os
158 \n""".format(filename=filename, dirname=dirname))
Kurt B. Kaiser11659ad2003-05-15 23:23:21 +0000159 interp.prepend_syspath(filename)
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000160 # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
161 # go to __stderr__. With subprocess, they go to the shell.
162 # Need to change streams in PyShell.ModifiedInterpreter.
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000163 interp.runcode(code)
Christian Heimesa156e092008-02-16 07:38:31 +0000164 return 'break'
David Scherer7aced172000-08-15 01:13:23 +0000165
166 def getfilename(self):
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000167 """Get source filename. If not saved, offer to save (or create) file
168
169 The debugger requires a source file. Make sure there is one, and that
170 the current version of the source buffer has been saved. If the user
171 declines to save or cancels the Save As dialog, return None.
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000172
173 If the user has configured IDLE for Autosave, the file will be
174 silently saved if it already exists and is dirty.
Kurt B. Kaiser8d1f11b2003-05-26 22:20:34 +0000175
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000176 """
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000177 filename = self.editwin.io.filename
David Scherer7aced172000-08-15 01:13:23 +0000178 if not self.editwin.get_saved():
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000179 autosave = idleConf.GetOption('main', 'General',
180 'autosave', type='bool')
181 if autosave and filename:
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000182 self.editwin.io.save(None)
183 else:
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400184 confirm = self.ask_save_dialog()
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000185 self.editwin.text.focus_set()
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400186 if confirm:
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000187 self.editwin.io.save(None)
188 filename = self.editwin.io.filename
189 else:
190 filename = None
David Scherer7aced172000-08-15 01:13:23 +0000191 return filename
192
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000193 def ask_save_dialog(self):
194 msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400195 confirm = tkMessageBox.askokcancel(title="Save Before Run or Check",
196 message=msg,
197 default=tkMessageBox.OK,
198 master=self.editwin.text)
199 return confirm
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000200
David Scherer7aced172000-08-15 01:13:23 +0000201 def errorbox(self, title, message):
202 # XXX This should really be a function of EditorWindow...
Ned Deily7a8e21a2011-01-29 23:34:19 +0000203 tkMessageBox.showerror(title, message, master=self.editwin.text)
David Scherer7aced172000-08-15 01:13:23 +0000204 self.editwin.text.focus_set()