blob: 41e6a59e6d5bf042b900fa900ad76c482ede5217 [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 re
22import string
23import tabnanny
24import tokenize
Georg Brandl14fc4272008-05-17 18:39:55 +000025import tkinter.messagebox as tkMessageBox
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000026from idlelib.EditorWindow import EditorWindow
Martin v. Löwis975a0792009-01-18 20:15:42 +000027from idlelib import PyShell, IOBinding
David Scherer7aced172000-08-15 01:13:23 +000028
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000029from idlelib.configHandler import idleConf
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +000030
David Scherer7aced172000-08-15 01:13:23 +000031indent_message = """Error: Inconsistent indentation detected!
32
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000331) Your indentation is outright incorrect (easy to fix), OR
David Scherer7aced172000-08-15 01:13:23 +000034
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +0000352) Your indentation mixes tabs and spaces.
David Scherer7aced172000-08-15 01:13:23 +000036
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +000037To fix case 2, change all tabs to spaces by using Edit->Select All followed \
38by Format->Untabify Region and specify the number of columns used by each tab.
39"""
Kurt B. Kaiser969de452002-06-12 03:28:57 +000040
David Scherer7aced172000-08-15 01:13:23 +000041class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000042
David Scherer7aced172000-08-15 01:13:23 +000043 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000044 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000045 ('Check Module', '<<check-module>>'),
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +000046 ('Run Module', '<<run-module>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000047
48 def __init__(self, editwin):
49 self.editwin = editwin
50 # Provide instance variables referenced by Debugger
51 # XXX This should be done differently
52 self.flist = self.editwin.flist
Thomas Wouters0e3f5912006-08-11 14:57:12 +000053 self.root = self.editwin.root
David Scherer7aced172000-08-15 01:13:23 +000054
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +000055 def check_module_event(self, event):
David Scherer7aced172000-08-15 01:13:23 +000056 filename = self.getfilename()
57 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +000058 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +000059 if not self.checksyntax(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000060 return 'break'
David Scherer7aced172000-08-15 01:13:23 +000061 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +000062 return 'break'
David Scherer7aced172000-08-15 01:13:23 +000063
64 def tabnanny(self, filename):
Martin v. Löwis975a0792009-01-18 20:15:42 +000065 # XXX: tabnanny should work on binary files as well
66 with open(filename, 'r', encoding='iso-8859-1') as f:
67 two_lines = f.readline() + f.readline()
68 encoding = IOBinding.coding_spec(two_lines)
69 if not encoding:
70 encoding = 'utf-8'
71 f = open(filename, 'r', encoding=encoding)
David Scherer7aced172000-08-15 01:13:23 +000072 try:
Kurt B. Kaiser05bab1e2002-09-18 03:05:19 +000073 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
Guido van Rossumb940e112007-01-10 16:19:56 +000074 except tokenize.TokenError as msg:
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +000075 msgtxt, (lineno, start) = msg
76 self.editwin.gotoline(lineno)
77 self.errorbox("Tabnanny Tokenizing Error",
78 "Token Error: %s" % msgtxt)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000079 return False
Guido van Rossumb940e112007-01-10 16:19:56 +000080 except tabnanny.NannyNag as nag:
David Scherer7aced172000-08-15 01:13:23 +000081 # The error messages from tabnanny are too confusing...
82 self.editwin.gotoline(nag.get_lineno())
83 self.errorbox("Tab/space error", indent_message)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000084 return False
85 return True
David Scherer7aced172000-08-15 01:13:23 +000086
87 def checksyntax(self, filename):
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000088 self.shell = shell = self.flist.open_shell()
89 saved_stream = shell.get_warning_stream()
90 shell.set_warning_stream(shell.stderr)
Martin v. Löwis975a0792009-01-18 20:15:42 +000091 f = open(filename, 'rb')
David Scherer7aced172000-08-15 01:13:23 +000092 source = f.read()
93 f.close()
Martin v. Löwis975a0792009-01-18 20:15:42 +000094 if b'\r' in source:
95 source = source.replace(b'\r\n', b'\n')
96 source = source.replace(b'\r', b'\n')
97 if source and source[-1] != ord(b'\n'):
98 source = source + b'\n'
Guido van Rossum33d26892007-08-05 15:29:28 +000099 editwin = self.editwin
100 text = editwin.text
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +0000101 text.tag_remove("ERROR", "1.0", "end")
David Scherer7aced172000-08-15 01:13:23 +0000102 try:
Guido van Rossum33d26892007-08-05 15:29:28 +0000103 # If successful, return the compiled code
104 return compile(source, filename, "exec")
105 except (SyntaxError, OverflowError) as value:
106 msg = value.msg or "<no detail available>"
107 lineno = value.lineno or 1
108 offset = value.offset or 0
109 if offset == 0:
110 lineno += 1 #mark end of offending line
111 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
112 editwin.colorize_syntax_error(text, pos)
113 self.errorbox("SyntaxError", "%-20s" % msg)
114 return False
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000115 finally:
116 shell.set_warning_stream(saved_stream)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000117
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +0000118 def run_module_event(self, event):
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000119 """Run the module after setting up the environment.
120
121 First check the syntax. If OK, make sure the shell is active and
122 then transfer the arguments, set the run environment's working
123 directory to the directory of the module being executed and also
124 add that directory to its sys.path if not already included.
125
126 """
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000127 filename = self.getfilename()
128 if not filename:
Christian Heimesa156e092008-02-16 07:38:31 +0000129 return 'break'
Kurt B. Kaiser0cd233f2005-08-23 03:25:38 +0000130 code = self.checksyntax(filename)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000131 if not code:
Christian Heimesa156e092008-02-16 07:38:31 +0000132 return 'break'
Thomas Wouters89f507f2006-12-13 04:49:30 +0000133 if not self.tabnanny(filename):
Christian Heimesa156e092008-02-16 07:38:31 +0000134 return 'break'
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000135 shell = self.shell
David Scherer7aced172000-08-15 01:13:23 +0000136 interp = shell.interp
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +0000137 if PyShell.use_subprocess:
138 shell.restart_shell()
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000139 dirname = os.path.dirname(filename)
Chui Tey5d2af632002-05-26 13:36:41 +0000140 # XXX Too often this discards arguments the user just set...
141 interp.runcommand("""if 1:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000142 _filename = %r
Chui Tey5d2af632002-05-26 13:36:41 +0000143 import sys as _sys
144 from os.path import basename as _basename
145 if (not _sys.argv or
146 _basename(_sys.argv[0]) != _basename(_filename)):
147 _sys.argv = [_filename]
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000148 import os as _os
Walter Dörwald70a6b492004-02-12 17:35:32 +0000149 _os.chdir(%r)
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000150 del _filename, _sys, _basename, _os
Walter Dörwald70a6b492004-02-12 17:35:32 +0000151 \n""" % (filename, dirname))
Kurt B. Kaiser11659ad2003-05-15 23:23:21 +0000152 interp.prepend_syspath(filename)
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +0000153 # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
154 # go to __stderr__. With subprocess, they go to the shell.
155 # Need to change streams in PyShell.ModifiedInterpreter.
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000156 interp.runcode(code)
Christian Heimesa156e092008-02-16 07:38:31 +0000157 return 'break'
David Scherer7aced172000-08-15 01:13:23 +0000158
159 def getfilename(self):
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000160 """Get source filename. If not saved, offer to save (or create) file
161
162 The debugger requires a source file. Make sure there is one, and that
163 the current version of the source buffer has been saved. If the user
164 declines to save or cancels the Save As dialog, return None.
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000165
166 If the user has configured IDLE for Autosave, the file will be
167 silently saved if it already exists and is dirty.
Kurt B. Kaiser8d1f11b2003-05-26 22:20:34 +0000168
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000169 """
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000170 filename = self.editwin.io.filename
David Scherer7aced172000-08-15 01:13:23 +0000171 if not self.editwin.get_saved():
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000172 autosave = idleConf.GetOption('main', 'General',
173 'autosave', type='bool')
174 if autosave and filename:
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000175 self.editwin.io.save(None)
176 else:
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400177 confirm = self.ask_save_dialog()
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000178 self.editwin.text.focus_set()
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400179 if confirm:
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000180 self.editwin.io.save(None)
181 filename = self.editwin.io.filename
182 else:
183 filename = None
David Scherer7aced172000-08-15 01:13:23 +0000184 return filename
185
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000186 def ask_save_dialog(self):
187 msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
Kurt B. Kaiser0a429822011-05-12 15:25:24 -0400188 confirm = tkMessageBox.askokcancel(title="Save Before Run or Check",
189 message=msg,
190 default=tkMessageBox.OK,
191 master=self.editwin.text)
192 return confirm
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000193
David Scherer7aced172000-08-15 01:13:23 +0000194 def errorbox(self, title, message):
195 # XXX This should really be a function of EditorWindow...
Ned Deily7a8e21a2011-01-29 23:34:19 +0000196 tkMessageBox.showerror(title, message, master=self.editwin.text)
David Scherer7aced172000-08-15 01:13:23 +0000197 self.editwin.text.focus_set()