blob: 68f9cd7d9585fc251dc0a8b6d8caa40a7c727ff9 [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.
David Scherer7aced172000-08-15 01:13:23 +00006It also runs the tabnanny to catch any inconsistent tabs.
7
Kurt B. Kaiser969de452002-06-12 03:28:57 +00008- Import module is equivalent to either import or reload of the
David Scherer7aced172000-08-15 01:13:23 +00009current module. The window must have been saved previously. The
10module is added to sys.modules, and is also added to the __main__
11namespace. Output goes to the shell window.
12
Kurt B. Kaiser969de452002-06-12 03:28:57 +000013- Run module does the same but executes the module's
David Scherer7aced172000-08-15 01:13:23 +000014code in the __main__ namespace.
15
Chui Tey5d2af632002-05-26 13:36:41 +000016XXX Redesign this interface (yet again) as follows:
17
18- Present a dialog box for ``Run script''
19
20- Allow specify command line arguments in the dialog box
21
22- Restart the interpreter when running a script
23
David Scherer7aced172000-08-15 01:13:23 +000024"""
25
26import sys
27import os
28import imp
29import tkMessageBox
30
31indent_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
44# XXX TBD Implement stop-execution KBK 11Jun02
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,
49# ('Check module', '<<check-module>>'),
50# ('Import module', '<<import-module>>'),
David Scherer7aced172000-08-15 01:13:23 +000051 ('Run script', '<<run-script>>'),
52 ]
53 ),
54 ]
55
56 def __init__(self, editwin):
57 self.editwin = editwin
58 # Provide instance variables referenced by Debugger
59 # XXX This should be done differently
60 self.flist = self.editwin.flist
61 self.root = self.flist.root
62
63 def check_module_event(self, event):
64 filename = self.getfilename()
65 if not filename:
66 return
67 if not self.tabnanny(filename):
68 return
69 if not self.checksyntax(filename):
70 return
71
72 def tabnanny(self, filename):
73 import tabnanny
74 import tokenize
75 tabnanny.reset_globals()
76 f = open(filename, 'r')
77 try:
78 tokenize.tokenize(f.readline, tabnanny.tokeneater)
79 except tokenize.TokenError, msg:
80 self.errorbox("Token error",
81 "Token error:\n%s" % str(msg))
82 return 0
83 except tabnanny.NannyNag, nag:
84 # The error messages from tabnanny are too confusing...
85 self.editwin.gotoline(nag.get_lineno())
86 self.errorbox("Tab/space error", indent_message)
87 return 0
88 return 1
89
90 def checksyntax(self, filename):
91 f = open(filename, 'r')
92 source = f.read()
93 f.close()
94 if '\r' in source:
95 import re
96 source = re.sub(r"\r\n", "\n", source)
97 if source and source[-1] != '\n':
98 source = source + '\n'
99 try:
100 compile(source, filename, "exec")
101 except (SyntaxError, OverflowError), err:
102 try:
103 msg, (errorfilename, lineno, offset, line) = err
104 if not errorfilename:
105 err.args = msg, (filename, lineno, offset, line)
106 err.filename = filename
107 except:
108 lineno = None
109 msg = "*** " + str(err)
110 if lineno:
111 self.editwin.gotoline(lineno)
112 self.errorbox("Syntax error",
113 "There's an error in your program:\n" + msg)
114 return 1
115
116 def import_module_event(self, event):
Chui Tey5d2af632002-05-26 13:36:41 +0000117 flist = self.editwin.flist
118 shell = flist.open_shell()
119 interp = shell.interp
120
David Scherer7aced172000-08-15 01:13:23 +0000121 filename = self.getfilename()
122 if not filename:
123 return
124
125 modname, ext = os.path.splitext(os.path.basename(filename))
David Scherer7aced172000-08-15 01:13:23 +0000126
127 dir = os.path.dirname(filename)
128 dir = os.path.normpath(os.path.abspath(dir))
David Scherer7aced172000-08-15 01:13:23 +0000129
Chui Tey5d2af632002-05-26 13:36:41 +0000130 interp.runcode("""if 1:
131 import sys as _sys
132 if %s not in _sys.path:
133 _sys.path.insert(0, %s)
134 if _sys.modules.get(%s):
135 del _sys
136 import %s
137 reload(%s)
138 else:
139 del _sys
140 import %s
141 \n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
David Scherer7aced172000-08-15 01:13:23 +0000142
143 def run_script_event(self, event):
144 filename = self.getfilename()
145 if not filename:
146 return
147
148 flist = self.editwin.flist
149 shell = flist.open_shell()
150 interp = shell.interp
Chui Tey5d2af632002-05-26 13:36:41 +0000151 # XXX Too often this discards arguments the user just set...
152 interp.runcommand("""if 1:
153 _filename = %s
154 import sys as _sys
155 from os.path import basename as _basename
156 if (not _sys.argv or
157 _basename(_sys.argv[0]) != _basename(_filename)):
158 _sys.argv = [_filename]
159 del _filename, _sys, _basename
160 \n""" % `filename`)
David Scherer7aced172000-08-15 01:13:23 +0000161 interp.execfile(filename)
162
163 def getfilename(self):
164 # Logic to make sure we have a saved filename
165 # XXX Better logic would offer to save!
166 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000167 name = (self.editwin.short_title() or
168 self.editwin.long_title() or
169 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000170 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000171 "The buffer for %s is not saved.\n" % name +
172 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000173 self.editwin.text.focus_set()
174 return
175 filename = self.editwin.io.filename
176 if not filename:
177 self.errorbox("No file name",
178 "This window has no file name")
179 return
180 return filename
181
182 def errorbox(self, title, message):
183 # XXX This should really be a function of EditorWindow...
184 tkMessageBox.showerror(title, message, master=self.editwin.text)
185 self.editwin.text.focus_set()