blob: b54dfc4c791931a8ec97e27c6aeae119d3a41c49 [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 keydefs = {
39 '<<check-module>>': ['<Alt-F5>', '<Meta-F5>'],
40 '<<import-module>>': ['<F5>'],
41 '<<run-script>>': ['<Control-F5>'],
42 }
Steven M. Gava42f6c642001-07-12 06:46:53 +000043
David Scherer7aced172000-08-15 01:13:23 +000044 menudefs = [
45 ('edit', [None,
46 ('Check module', '<<check-module>>'),
47 ('Import module', '<<import-module>>'),
48 ('Run script', '<<run-script>>'),
49 ]
50 ),
51 ]
52
53 def __init__(self, editwin):
54 self.editwin = editwin
55 # Provide instance variables referenced by Debugger
56 # XXX This should be done differently
57 self.flist = self.editwin.flist
58 self.root = self.flist.root
59
60 def check_module_event(self, event):
61 filename = self.getfilename()
62 if not filename:
63 return
64 if not self.tabnanny(filename):
65 return
66 if not self.checksyntax(filename):
67 return
68
69 def tabnanny(self, filename):
70 import tabnanny
71 import tokenize
72 tabnanny.reset_globals()
73 f = open(filename, 'r')
74 try:
75 tokenize.tokenize(f.readline, tabnanny.tokeneater)
76 except tokenize.TokenError, msg:
77 self.errorbox("Token error",
78 "Token error:\n%s" % str(msg))
79 return 0
80 except tabnanny.NannyNag, nag:
81 # The error messages from tabnanny are too confusing...
82 self.editwin.gotoline(nag.get_lineno())
83 self.errorbox("Tab/space error", indent_message)
84 return 0
85 return 1
86
87 def checksyntax(self, filename):
88 f = open(filename, 'r')
89 source = f.read()
90 f.close()
91 if '\r' in source:
92 import re
93 source = re.sub(r"\r\n", "\n", source)
94 if source and source[-1] != '\n':
95 source = source + '\n'
96 try:
97 compile(source, filename, "exec")
98 except (SyntaxError, OverflowError), err:
99 try:
100 msg, (errorfilename, lineno, offset, line) = err
101 if not errorfilename:
102 err.args = msg, (filename, lineno, offset, line)
103 err.filename = filename
104 except:
105 lineno = None
106 msg = "*** " + str(err)
107 if lineno:
108 self.editwin.gotoline(lineno)
109 self.errorbox("Syntax error",
110 "There's an error in your program:\n" + msg)
111 return 1
112
113 def import_module_event(self, event):
114 filename = self.getfilename()
115 if not filename:
116 return
117
118 modname, ext = os.path.splitext(os.path.basename(filename))
119 if sys.modules.has_key(modname):
120 mod = sys.modules[modname]
121 else:
122 mod = imp.new_module(modname)
123 sys.modules[modname] = mod
124 mod.__file__ = filename
125 setattr(sys.modules['__main__'], modname, mod)
126
127 dir = os.path.dirname(filename)
128 dir = os.path.normpath(os.path.abspath(dir))
129 if dir not in sys.path:
130 sys.path.insert(0, dir)
131
132 flist = self.editwin.flist
133 shell = flist.open_shell()
134 interp = shell.interp
135 interp.runcode("reload(%s)" % modname)
136
137 def run_script_event(self, event):
138 filename = self.getfilename()
139 if not filename:
140 return
141
142 flist = self.editwin.flist
143 shell = flist.open_shell()
144 interp = shell.interp
145 if (not sys.argv or
146 os.path.basename(sys.argv[0]) != os.path.basename(filename)):
147 # XXX Too often this discards arguments the user just set...
148 sys.argv = [filename]
149 interp.execfile(filename)
150
151 def getfilename(self):
152 # Logic to make sure we have a saved filename
153 # XXX Better logic would offer to save!
154 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000155 name = (self.editwin.short_title() or
156 self.editwin.long_title() or
157 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000158 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000159 "The buffer for %s is not saved.\n" % name +
160 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000161 self.editwin.text.focus_set()
162 return
163 filename = self.editwin.io.filename
164 if not filename:
165 self.errorbox("No file name",
166 "This window has no file name")
167 return
168 return filename
169
170 def errorbox(self, title, message):
171 # XXX This should really be a function of EditorWindow...
172 tkMessageBox.showerror(title, message, master=self.editwin.text)
173 self.editwin.text.focus_set()