blob: 00f533bf0df6b888758219cc9bc4bbc6c8419e76 [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. Kaiser92b5ca32002-12-17 21:16:12 +000020import re
21import string
22import tabnanny
23import tokenize
David Scherer7aced172000-08-15 01:13:23 +000024import tkMessageBox
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +000025import PyShell
David Scherer7aced172000-08-15 01:13:23 +000026
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +000027from configHandler import idleConf
28
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000029IDENTCHARS = string.ascii_letters + string.digits + "_"
30
David Scherer7aced172000-08-15 01:13:23 +000031indent_message = """Error: Inconsistent indentation detected!
32
33This means that either:
34
Chui Tey5d2af632002-05-26 13:36:41 +0000351) your indentation is outright incorrect (easy to fix), or
David Scherer7aced172000-08-15 01:13:23 +000036
Chui Tey5d2af632002-05-26 13:36:41 +0000372) your indentation mixes tabs and spaces in a way that depends on \
David Scherer7aced172000-08-15 01:13:23 +000038how many spaces a tab is worth.
39
40To fix case 2, change all tabs to spaces by using Select All followed \
41by Untabify Region (both in the Edit menu)."""
42
Kurt B. Kaiser969de452002-06-12 03:28:57 +000043
David Scherer7aced172000-08-15 01:13:23 +000044class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000045
David Scherer7aced172000-08-15 01:13:23 +000046 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000047 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000048 ('Check Module', '<<check-module>>'),
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +000049 ('Run Module', '<<run-module>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000050
51 def __init__(self, editwin):
52 self.editwin = editwin
53 # Provide instance variables referenced by Debugger
54 # XXX This should be done differently
55 self.flist = self.editwin.flist
56 self.root = self.flist.root
57
58 def check_module_event(self, event):
59 filename = self.getfilename()
60 if not filename:
61 return
62 if not self.tabnanny(filename):
63 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000064 self.checksyntax(filename)
David Scherer7aced172000-08-15 01:13:23 +000065
66 def tabnanny(self, filename):
David Scherer7aced172000-08-15 01:13:23 +000067 f = open(filename, 'r')
68 try:
Kurt B. Kaiser05bab1e2002-09-18 03:05:19 +000069 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
David Scherer7aced172000-08-15 01:13:23 +000070 except tokenize.TokenError, msg:
Neal Norwitz6453c1f2002-11-30 19:18:46 +000071 self.errorbox("Token error", "Token error:\n%s" % msg)
72 return False
David Scherer7aced172000-08-15 01:13:23 +000073 except tabnanny.NannyNag, nag:
74 # The error messages from tabnanny are too confusing...
75 self.editwin.gotoline(nag.get_lineno())
76 self.errorbox("Tab/space error", indent_message)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000077 return False
78 return True
David Scherer7aced172000-08-15 01:13:23 +000079
80 def checksyntax(self, filename):
81 f = open(filename, 'r')
82 source = f.read()
83 f.close()
84 if '\r' in source:
David Scherer7aced172000-08-15 01:13:23 +000085 source = re.sub(r"\r\n", "\n", source)
86 if source and source[-1] != '\n':
87 source = source + '\n'
88 try:
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000089 # If successful, return the compiled code
90 return compile(source, filename, "exec")
David Scherer7aced172000-08-15 01:13:23 +000091 except (SyntaxError, OverflowError), err:
92 try:
93 msg, (errorfilename, lineno, offset, line) = err
94 if not errorfilename:
95 err.args = msg, (filename, lineno, offset, line)
96 err.filename = filename
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000097 self.colorize_syntax_error(msg, lineno, offset)
David Scherer7aced172000-08-15 01:13:23 +000098 except:
David Scherer7aced172000-08-15 01:13:23 +000099 msg = "*** " + str(err)
David Scherer7aced172000-08-15 01:13:23 +0000100 self.errorbox("Syntax error",
101 "There's an error in your program:\n" + msg)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000102 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000103
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000104 def colorize_syntax_error(self, msg, lineno, offset):
105 text = self.editwin.text
106 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
107 text.tag_add("ERROR", pos)
108 char = text.get(pos)
109 if char and char in IDENTCHARS:
110 text.tag_add("ERROR", pos + " wordstart", pos)
111 if '\n' == text.get(pos): # error at line end
112 text.mark_set("insert", pos)
113 else:
114 text.mark_set("insert", pos + "+1c")
115 text.see(pos)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000116
Kurt B. Kaisereb9637e2003-01-26 04:17:16 +0000117 def run_module_event(self, event):
118 "Check syntax, if ok run the module in the shell top level"
David Scherer7aced172000-08-15 01:13:23 +0000119 filename = self.getfilename()
120 if not filename:
121 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000122 code = self.checksyntax(filename)
123 if not code:
124 return
David Scherer7aced172000-08-15 01:13:23 +0000125 flist = self.editwin.flist
126 shell = flist.open_shell()
127 interp = shell.interp
Kurt B. Kaiser7f38ec02003-05-15 03:19:42 +0000128 if PyShell.use_subprocess:
129 shell.restart_shell()
Chui Tey5d2af632002-05-26 13:36:41 +0000130 # XXX Too often this discards arguments the user just set...
131 interp.runcommand("""if 1:
132 _filename = %s
133 import sys as _sys
134 from os.path import basename as _basename
135 if (not _sys.argv or
136 _basename(_sys.argv[0]) != _basename(_filename)):
137 _sys.argv = [_filename]
Kurt B. Kaiser11659ad2003-05-15 23:23:21 +0000138 del _filename, _sys, _basename
139 \n""" % `filename`)
140 interp.prepend_syspath(filename)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000141 interp.runcode(code)
David Scherer7aced172000-08-15 01:13:23 +0000142
143 def getfilename(self):
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000144 """Get source filename. If not saved, offer to save (or create) file
145
146 The debugger requires a source file. Make sure there is one, and that
147 the current version of the source buffer has been saved. If the user
148 declines to save or cancels the Save As dialog, return None.
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000149
150 If the user has configured IDLE for Autosave, the file will be
151 silently saved if it already exists and is dirty.
152
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000153 """
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000154 filename = self.editwin.io.filename
David Scherer7aced172000-08-15 01:13:23 +0000155 if not self.editwin.get_saved():
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000156 autosave = idleConf.GetOption('main', 'General',
157 'autosave', type='bool')
158 if autosave and filename:
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000159 self.editwin.io.save(None)
160 else:
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000161 reply = self.ask_save_dialog()
162 self.editwin.text.focus_set()
163 if reply == "ok":
164 self.editwin.io.save(None)
165 filename = self.editwin.io.filename
166 else:
167 filename = None
David Scherer7aced172000-08-15 01:13:23 +0000168 return filename
169
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000170 def ask_save_dialog(self):
171 msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
172 mb = tkMessageBox.Message(title="Save Before Run or Check",
173 message=msg,
174 icon=tkMessageBox.QUESTION,
175 type=tkMessageBox.OKCANCEL,
176 default=tkMessageBox.OK,
177 master=self.editwin.text)
178 return mb.show()
179
David Scherer7aced172000-08-15 01:13:23 +0000180 def errorbox(self, title, message):
181 # XXX This should really be a function of EditorWindow...
182 tkMessageBox.showerror(title, message, master=self.editwin.text)
183 self.editwin.text.focus_set()