blob: 2b3bf99267bea715a59830293a447676f9eecabf [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
17"""
18
19import sys
20import os
21import imp
22import tkMessageBox
23
24indent_message = """Error: Inconsistent indentation detected!
25
26This means that either:
27
28(1) your indentation is outright incorrect (easy to fix), or
29
30(2) your indentation mixes tabs and spaces in a way that depends on \
31how many spaces a tab is worth.
32
33To fix case 2, change all tabs to spaces by using Select All followed \
34by Untabify Region (both in the Edit menu)."""
35
36class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000037
David Scherer7aced172000-08-15 01:13:23 +000038 menudefs = [
39 ('edit', [None,
40 ('Check module', '<<check-module>>'),
41 ('Import module', '<<import-module>>'),
42 ('Run script', '<<run-script>>'),
43 ]
44 ),
45 ]
46
47 def __init__(self, editwin):
48 self.editwin = editwin
49 # Provide instance variables referenced by Debugger
50 # XXX This should be done differently
51 self.flist = self.editwin.flist
52 self.root = self.flist.root
53
54 def check_module_event(self, event):
55 filename = self.getfilename()
56 if not filename:
57 return
58 if not self.tabnanny(filename):
59 return
60 if not self.checksyntax(filename):
61 return
62
63 def tabnanny(self, filename):
64 import tabnanny
65 import tokenize
66 tabnanny.reset_globals()
67 f = open(filename, 'r')
68 try:
69 tokenize.tokenize(f.readline, tabnanny.tokeneater)
70 except tokenize.TokenError, msg:
71 self.errorbox("Token error",
72 "Token error:\n%s" % str(msg))
73 return 0
74 except tabnanny.NannyNag, nag:
75 # The error messages from tabnanny are too confusing...
76 self.editwin.gotoline(nag.get_lineno())
77 self.errorbox("Tab/space error", indent_message)
78 return 0
79 return 1
80
81 def checksyntax(self, filename):
82 f = open(filename, 'r')
83 source = f.read()
84 f.close()
85 if '\r' in source:
86 import re
87 source = re.sub(r"\r\n", "\n", source)
88 if source and source[-1] != '\n':
89 source = source + '\n'
90 try:
91 compile(source, filename, "exec")
92 except (SyntaxError, OverflowError), err:
93 try:
94 msg, (errorfilename, lineno, offset, line) = err
95 if not errorfilename:
96 err.args = msg, (filename, lineno, offset, line)
97 err.filename = filename
98 except:
99 lineno = None
100 msg = "*** " + str(err)
101 if lineno:
102 self.editwin.gotoline(lineno)
103 self.errorbox("Syntax error",
104 "There's an error in your program:\n" + msg)
105 return 1
106
107 def import_module_event(self, event):
108 filename = self.getfilename()
109 if not filename:
110 return
111
112 modname, ext = os.path.splitext(os.path.basename(filename))
113 if sys.modules.has_key(modname):
114 mod = sys.modules[modname]
115 else:
116 mod = imp.new_module(modname)
117 sys.modules[modname] = mod
118 mod.__file__ = filename
119 setattr(sys.modules['__main__'], modname, mod)
120
121 dir = os.path.dirname(filename)
122 dir = os.path.normpath(os.path.abspath(dir))
123 if dir not in sys.path:
124 sys.path.insert(0, dir)
125
126 flist = self.editwin.flist
127 shell = flist.open_shell()
128 interp = shell.interp
129 interp.runcode("reload(%s)" % modname)
130
131 def run_script_event(self, event):
132 filename = self.getfilename()
133 if not filename:
134 return
135
136 flist = self.editwin.flist
137 shell = flist.open_shell()
138 interp = shell.interp
139 if (not sys.argv or
140 os.path.basename(sys.argv[0]) != os.path.basename(filename)):
141 # XXX Too often this discards arguments the user just set...
142 sys.argv = [filename]
143 interp.execfile(filename)
144
145 def getfilename(self):
146 # Logic to make sure we have a saved filename
147 # XXX Better logic would offer to save!
148 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000149 name = (self.editwin.short_title() or
150 self.editwin.long_title() or
151 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000152 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000153 "The buffer for %s is not saved.\n" % name +
154 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000155 self.editwin.text.focus_set()
156 return
157 filename = self.editwin.io.filename
158 if not filename:
159 self.errorbox("No file name",
160 "This window has no file name")
161 return
162 return filename
163
164 def errorbox(self, title, message):
165 # XXX This should really be a function of EditorWindow...
166 tkMessageBox.showerror(title, message, master=self.editwin.text)
167 self.editwin.text.focus_set()