blob: 906b4f2052688c22ce9941e011b6851e1788f9d8 [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001"""Extension to execute code outside the Python shell window.
2
3This adds the following commands (to the Edit menu, until there's a
4separate Python menu):
5
6- Check module (Alt-F5) does a full syntax check of the current module.
7It also runs the tabnanny to catch any inconsistent tabs.
8
9- Import module (F5) is equivalent to either import or reload of the
10current module. The window must have been saved previously. The
11module is added to sys.modules, and is also added to the __main__
12namespace. Output goes to the shell window.
13
14- Run module (Control-F5) does the same but executes the module's
15code in the __main__ namespace.
16
Chui Tey5d2af632002-05-26 13:36:41 +000017XXX Redesign this interface (yet again) as follows:
18
19- Present a dialog box for ``Run script''
20
21- Allow specify command line arguments in the dialog box
22
23- Restart the interpreter when running a script
24
David Scherer7aced172000-08-15 01:13:23 +000025"""
26
27import sys
28import os
29import imp
30import tkMessageBox
31
32indent_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
44class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000045
David Scherer7aced172000-08-15 01:13:23 +000046 menudefs = [
47 ('edit', [None,
48 ('Check module', '<<check-module>>'),
49 ('Import module', '<<import-module>>'),
50 ('Run script', '<<run-script>>'),
51 ]
52 ),
53 ]
54
55 def __init__(self, editwin):
56 self.editwin = editwin
57 # Provide instance variables referenced by Debugger
58 # XXX This should be done differently
59 self.flist = self.editwin.flist
60 self.root = self.flist.root
61
62 def check_module_event(self, event):
63 filename = self.getfilename()
64 if not filename:
65 return
66 if not self.tabnanny(filename):
67 return
68 if not self.checksyntax(filename):
69 return
70
71 def tabnanny(self, filename):
72 import tabnanny
73 import tokenize
74 tabnanny.reset_globals()
75 f = open(filename, 'r')
76 try:
77 tokenize.tokenize(f.readline, tabnanny.tokeneater)
78 except tokenize.TokenError, msg:
79 self.errorbox("Token error",
80 "Token error:\n%s" % str(msg))
81 return 0
82 except tabnanny.NannyNag, nag:
83 # The error messages from tabnanny are too confusing...
84 self.editwin.gotoline(nag.get_lineno())
85 self.errorbox("Tab/space error", indent_message)
86 return 0
87 return 1
88
89 def checksyntax(self, filename):
90 f = open(filename, 'r')
91 source = f.read()
92 f.close()
93 if '\r' in source:
94 import re
95 source = re.sub(r"\r\n", "\n", source)
96 if source and source[-1] != '\n':
97 source = source + '\n'
98 try:
99 compile(source, filename, "exec")
100 except (SyntaxError, OverflowError), err:
101 try:
102 msg, (errorfilename, lineno, offset, line) = err
103 if not errorfilename:
104 err.args = msg, (filename, lineno, offset, line)
105 err.filename = filename
106 except:
107 lineno = None
108 msg = "*** " + str(err)
109 if lineno:
110 self.editwin.gotoline(lineno)
111 self.errorbox("Syntax error",
112 "There's an error in your program:\n" + msg)
113 return 1
114
115 def import_module_event(self, event):
Chui Tey5d2af632002-05-26 13:36:41 +0000116 flist = self.editwin.flist
117 shell = flist.open_shell()
118 interp = shell.interp
119
David Scherer7aced172000-08-15 01:13:23 +0000120 filename = self.getfilename()
121 if not filename:
122 return
123
124 modname, ext = os.path.splitext(os.path.basename(filename))
David Scherer7aced172000-08-15 01:13:23 +0000125
126 dir = os.path.dirname(filename)
127 dir = os.path.normpath(os.path.abspath(dir))
David Scherer7aced172000-08-15 01:13:23 +0000128
Chui Tey5d2af632002-05-26 13:36:41 +0000129 interp.runcode("""if 1:
130 import sys as _sys
131 if %s not in _sys.path:
132 _sys.path.insert(0, %s)
133 if _sys.modules.get(%s):
134 del _sys
135 import %s
136 reload(%s)
137 else:
138 del _sys
139 import %s
140 \n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
David Scherer7aced172000-08-15 01:13:23 +0000141
142 def run_script_event(self, event):
143 filename = self.getfilename()
144 if not filename:
145 return
146
147 flist = self.editwin.flist
148 shell = flist.open_shell()
149 interp = shell.interp
Chui Tey5d2af632002-05-26 13:36:41 +0000150 # XXX Too often this discards arguments the user just set...
151 interp.runcommand("""if 1:
152 _filename = %s
153 import sys as _sys
154 from os.path import basename as _basename
155 if (not _sys.argv or
156 _basename(_sys.argv[0]) != _basename(_filename)):
157 _sys.argv = [_filename]
158 del _filename, _sys, _basename
159 \n""" % `filename`)
David Scherer7aced172000-08-15 01:13:23 +0000160 interp.execfile(filename)
161
162 def getfilename(self):
163 # Logic to make sure we have a saved filename
164 # XXX Better logic would offer to save!
165 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000166 name = (self.editwin.short_title() or
167 self.editwin.long_title() or
168 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000169 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000170 "The buffer for %s is not saved.\n" % name +
171 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000172 self.editwin.text.focus_set()
173 return
174 filename = self.editwin.io.filename
175 if not filename:
176 self.errorbox("No file name",
177 "This window has no file name")
178 return
179 return filename
180
181 def errorbox(self, title, message):
182 # XXX This should really be a function of EditorWindow...
183 tkMessageBox.showerror(title, message, master=self.editwin.text)
184 self.editwin.text.focus_set()