blob: 8ab8e7afcdf7262557edc699f7a985ada2914c0d [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
14- Present a dialog box for ``Run script''
15
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
25
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000026IDENTCHARS = string.ascii_letters + string.digits + "_"
27
David Scherer7aced172000-08-15 01:13:23 +000028indent_message = """Error: Inconsistent indentation detected!
29
30This means that either:
31
Chui Tey5d2af632002-05-26 13:36:41 +0000321) your indentation is outright incorrect (easy to fix), or
David Scherer7aced172000-08-15 01:13:23 +000033
Chui Tey5d2af632002-05-26 13:36:41 +0000342) your indentation mixes tabs and spaces in a way that depends on \
David Scherer7aced172000-08-15 01:13:23 +000035how many spaces a tab is worth.
36
37To fix case 2, change all tabs to spaces by using Select All followed \
38by Untabify Region (both in the Edit menu)."""
39
Kurt B. Kaiser969de452002-06-12 03:28:57 +000040
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000041# XXX 11Jun02 KBK TBD Implement stop-execution
42
David Scherer7aced172000-08-15 01:13:23 +000043class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000044
David Scherer7aced172000-08-15 01:13:23 +000045 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000046 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000047 ('Check Module', '<<check-module>>'),
48 ('Run Script', '<<run-script>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000049
50 def __init__(self, editwin):
51 self.editwin = editwin
52 # Provide instance variables referenced by Debugger
53 # XXX This should be done differently
54 self.flist = self.editwin.flist
55 self.root = self.flist.root
56
57 def check_module_event(self, event):
58 filename = self.getfilename()
59 if not filename:
60 return
61 if not self.tabnanny(filename):
62 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000063 self.checksyntax(filename)
David Scherer7aced172000-08-15 01:13:23 +000064
65 def tabnanny(self, filename):
David Scherer7aced172000-08-15 01:13:23 +000066 f = open(filename, 'r')
67 try:
Kurt B. Kaiser05bab1e2002-09-18 03:05:19 +000068 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
David Scherer7aced172000-08-15 01:13:23 +000069 except tokenize.TokenError, msg:
Neal Norwitz6453c1f2002-11-30 19:18:46 +000070 self.errorbox("Token error", "Token error:\n%s" % msg)
71 return False
David Scherer7aced172000-08-15 01:13:23 +000072 except tabnanny.NannyNag, nag:
73 # The error messages from tabnanny are too confusing...
74 self.editwin.gotoline(nag.get_lineno())
75 self.errorbox("Tab/space error", indent_message)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000076 return False
77 return True
David Scherer7aced172000-08-15 01:13:23 +000078
79 def checksyntax(self, filename):
80 f = open(filename, 'r')
81 source = f.read()
82 f.close()
83 if '\r' in source:
David Scherer7aced172000-08-15 01:13:23 +000084 source = re.sub(r"\r\n", "\n", source)
85 if source and source[-1] != '\n':
86 source = source + '\n'
87 try:
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000088 # If successful, return the compiled code
89 return compile(source, filename, "exec")
David Scherer7aced172000-08-15 01:13:23 +000090 except (SyntaxError, OverflowError), err:
91 try:
92 msg, (errorfilename, lineno, offset, line) = err
93 if not errorfilename:
94 err.args = msg, (filename, lineno, offset, line)
95 err.filename = filename
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000096 self.colorize_syntax_error(msg, lineno, offset)
David Scherer7aced172000-08-15 01:13:23 +000097 except:
David Scherer7aced172000-08-15 01:13:23 +000098 msg = "*** " + str(err)
David Scherer7aced172000-08-15 01:13:23 +000099 self.errorbox("Syntax error",
100 "There's an error in your program:\n" + msg)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000101 return False
102
103 def colorize_syntax_error(self, msg, lineno, offset):
104 text = self.editwin.text
105 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
106 text.tag_add("ERROR", pos)
107 char = text.get(pos)
108 if char and char in IDENTCHARS:
109 text.tag_add("ERROR", pos + " wordstart", pos)
110 if '\n' == text.get(pos): # error at line end
111 text.mark_set("insert", pos)
112 else:
113 text.mark_set("insert", pos + "+1c")
114 text.see(pos)
115
David Scherer7aced172000-08-15 01:13:23 +0000116 def run_script_event(self, event):
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000117 "Check syntax, if ok run the script in the shell top level"
David Scherer7aced172000-08-15 01:13:23 +0000118 filename = self.getfilename()
119 if not filename:
120 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000121 code = self.checksyntax(filename)
122 if not code:
123 return
David Scherer7aced172000-08-15 01:13:23 +0000124 flist = self.editwin.flist
125 shell = flist.open_shell()
126 interp = shell.interp
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000127 if interp.tkconsole.executing:
128 interp.display_executing_dialog()
129 return
130 interp.restart_subprocess()
Chui Tey5d2af632002-05-26 13:36:41 +0000131 # XXX Too often this discards arguments the user just set...
132 interp.runcommand("""if 1:
133 _filename = %s
134 import sys as _sys
135 from os.path import basename as _basename
136 if (not _sys.argv or
137 _basename(_sys.argv[0]) != _basename(_filename)):
138 _sys.argv = [_filename]
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000139 del _filename, _sys, _basename
Chui Tey5d2af632002-05-26 13:36:41 +0000140 \n""" % `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.
149 """
David Scherer7aced172000-08-15 01:13:23 +0000150 if not self.editwin.get_saved():
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000151 msg = """Source Must Be Saved
152 OK to Save?"""
153 mb = tkMessageBox.Message(
154 title="Save Before Run or Check",
155 message=msg,
156 icon=tkMessageBox.QUESTION,
157 type=tkMessageBox.OKCANCEL,
158 master=self.editwin.text)
159 reply = mb.show()
160 if reply == "ok":
161 self.editwin.io.save(None)
162 else:
163 return None
164 # filename is None if file doesn't exist
David Scherer7aced172000-08-15 01:13:23 +0000165 filename = self.editwin.io.filename
Kurt B. Kaiserd5e1cef2002-12-19 03:25:34 +0000166 self.editwin.text.focus_set()
David Scherer7aced172000-08-15 01:13:23 +0000167 return filename
168
169 def errorbox(self, title, message):
170 # XXX This should really be a function of EditorWindow...
171 tkMessageBox.showerror(title, message, master=self.editwin.text)
172 self.editwin.text.focus_set()