blob: fa9ecbc5234bcd8132f8c771cb3e7e1573ee9117 [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
David Scherer7aced172000-08-15 01:13:23 +000025import tkMessageBox
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +000026import PyShell
David Scherer7aced172000-08-15 01:13:23 +000027
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +000028from configHandler import idleConf
29
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000030IDENTCHARS = string.ascii_letters + string.digits + "_"
31
David Scherer7aced172000-08-15 01:13:23 +000032indent_message = """Error: Inconsistent indentation detected!
33
34This means that either:
35
Chui Tey5d2af632002-05-26 13:36:41 +0000361) your indentation is outright incorrect (easy to fix), or
David Scherer7aced172000-08-15 01:13:23 +000037
Chui Tey5d2af632002-05-26 13:36:41 +0000382) your indentation mixes tabs and spaces in a way that depends on \
David Scherer7aced172000-08-15 01:13:23 +000039how many spaces a tab is worth.
40
41To fix case 2, change all tabs to spaces by using Select All followed \
42by Untabify Region (both in the Edit menu)."""
43
Kurt B. Kaiser969de452002-06-12 03:28:57 +000044
David Scherer7aced172000-08-15 01:13:23 +000045class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000046
David Scherer7aced172000-08-15 01:13:23 +000047 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000048 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000049 ('Check Module', '<<check-module>>'),
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +000050 ('Run Module', '<<run-module>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000051
52 def __init__(self, editwin):
53 self.editwin = editwin
54 # Provide instance variables referenced by Debugger
55 # XXX This should be done differently
56 self.flist = self.editwin.flist
57 self.root = self.flist.root
58
59 def check_module_event(self, event):
60 filename = self.getfilename()
61 if not filename:
62 return
63 if not self.tabnanny(filename):
64 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000065 self.checksyntax(filename)
David Scherer7aced172000-08-15 01:13:23 +000066
67 def tabnanny(self, filename):
David Scherer7aced172000-08-15 01:13:23 +000068 f = open(filename, 'r')
69 try:
Kurt B. Kaiser05bab1e2002-09-18 03:05:19 +000070 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
David Scherer7aced172000-08-15 01:13:23 +000071 except tokenize.TokenError, msg:
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +000072 msgtxt, (lineno, start) = msg
73 self.editwin.gotoline(lineno)
74 self.errorbox("Tabnanny Tokenizing Error",
75 "Token Error: %s" % msgtxt)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000076 return False
David Scherer7aced172000-08-15 01:13:23 +000077 except tabnanny.NannyNag, nag:
78 # The error messages from tabnanny are too confusing...
79 self.editwin.gotoline(nag.get_lineno())
80 self.errorbox("Tab/space error", indent_message)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000081 return False
82 return True
David Scherer7aced172000-08-15 01:13:23 +000083
84 def checksyntax(self, filename):
85 f = open(filename, 'r')
86 source = f.read()
87 f.close()
88 if '\r' in source:
David Scherer7aced172000-08-15 01:13:23 +000089 source = re.sub(r"\r\n", "\n", source)
90 if source and source[-1] != '\n':
91 source = source + '\n'
Kurt B. Kaiser3f8ace92003-06-05 02:38:32 +000092 text = self.editwin.text
93 text.tag_remove("ERROR", "1.0", "end")
David Scherer7aced172000-08-15 01:13:23 +000094 try:
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000095 # If successful, return the compiled code
96 return compile(source, filename, "exec")
David Scherer7aced172000-08-15 01:13:23 +000097 except (SyntaxError, OverflowError), err:
98 try:
99 msg, (errorfilename, lineno, offset, line) = err
100 if not errorfilename:
101 err.args = msg, (filename, lineno, offset, line)
102 err.filename = filename
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000103 self.colorize_syntax_error(msg, lineno, offset)
David Scherer7aced172000-08-15 01:13:23 +0000104 except:
David Scherer7aced172000-08-15 01:13:23 +0000105 msg = "*** " + str(err)
David Scherer7aced172000-08-15 01:13:23 +0000106 self.errorbox("Syntax error",
107 "There's an error in your program:\n" + msg)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000108 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000109
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000110 def colorize_syntax_error(self, msg, lineno, offset):
111 text = self.editwin.text
112 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
113 text.tag_add("ERROR", pos)
114 char = text.get(pos)
115 if char and char in IDENTCHARS:
116 text.tag_add("ERROR", pos + " wordstart", pos)
117 if '\n' == text.get(pos): # error at line end
118 text.mark_set("insert", pos)
119 else:
120 text.mark_set("insert", pos + "+1c")
121 text.see(pos)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000122
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +0000123 def run_module_event(self, event):
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000124 """Run the module after setting up the environment.
125
126 First check the syntax. If OK, make sure the shell is active and
127 then transfer the arguments, set the run environment's working
128 directory to the directory of the module being executed and also
129 add that directory to its sys.path if not already included.
130
131 """
David Scherer7aced172000-08-15 01:13:23 +0000132 filename = self.getfilename()
133 if not filename:
134 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000135 code = self.checksyntax(filename)
136 if not code:
137 return
David Scherer7aced172000-08-15 01:13:23 +0000138 flist = self.editwin.flist
139 shell = flist.open_shell()
140 interp = shell.interp
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +0000141 if PyShell.use_subprocess:
142 shell.restart_shell()
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000143 dirname = os.path.dirname(filename)
Chui Tey5d2af632002-05-26 13:36:41 +0000144 # XXX Too often this discards arguments the user just set...
145 interp.runcommand("""if 1:
146 _filename = %s
147 import sys as _sys
148 from os.path import basename as _basename
149 if (not _sys.argv or
150 _basename(_sys.argv[0]) != _basename(_filename)):
151 _sys.argv = [_filename]
Kurt B. Kaiserce5b6d52003-05-31 23:44:18 +0000152 import os as _os
153 _os.chdir(%s)
154 del _filename, _sys, _basename, _os
155 \n""" % (`filename`, `dirname`))
Kurt B. Kaiser11659ad2003-05-15 23:23:21 +0000156 interp.prepend_syspath(filename)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000157 interp.runcode(code)
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. Kaiser6c638b62003-05-26 06:23:10 +0000177 reply = self.ask_save_dialog()
178 self.editwin.text.focus_set()
179 if reply == "ok":
180 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?"
188 mb = tkMessageBox.Message(title="Save Before Run or Check",
189 message=msg,
190 icon=tkMessageBox.QUESTION,
191 type=tkMessageBox.OKCANCEL,
192 default=tkMessageBox.OK,
193 master=self.editwin.text)
194 return mb.show()
195
David Scherer7aced172000-08-15 01:13:23 +0000196 def errorbox(self, title, message):
197 # XXX This should really be a function of EditorWindow...
198 tkMessageBox.showerror(title, message, master=self.editwin.text)
199 self.editwin.text.focus_set()