blob: b54dfc4c791931a8ec97e27c6aeae119d3a41c49 [file] [log] [blame]
Guido van Rossum9f425841999-01-09 05:00:17 +00001"""Extension to execute code outside the Python shell window.
2
Guido van Rossum8c95c272000-02-22 00:19:58 +00003This 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.
Guido van Rossum9f425841999-01-09 05:00:17 +00008
Guido van Rossum1f3de5d1999-04-22 22:27:40 +00009- Import module (F5) is equivalent to either import or reload of the
Guido van Rossum9f425841999-01-09 05:00:17 +000010current module. The window must have been saved previously. The
Guido van Rossum1f3de5d1999-04-22 22:27:40 +000011module is added to sys.modules, and is also added to the __main__
12namespace. Output goes to the shell window.
Guido van Rossum9f425841999-01-09 05:00:17 +000013
Guido van Rossum1f3de5d1999-04-22 22:27:40 +000014- Run module (Control-F5) does the same but executes the module's
15code in the __main__ namespace.
Guido van Rossum9f425841999-01-09 05:00:17 +000016
17"""
18
19import sys
Guido van Rossum504b0bf1999-01-02 21:28:54 +000020import os
21import imp
Guido van Rossum9f425841999-01-09 05:00:17 +000022import tkMessageBox
Guido van Rossum825df2a1999-03-29 14:52:28 +000023
Guido van Rossum8c95c272000-02-22 00:19:58 +000024indent_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)."""
Guido van Rossum504b0bf1999-01-02 21:28:54 +000035
36class ScriptBinding:
Tim Peters70c43782001-01-17 08:48:39 +000037
Guido van Rossum9f425841999-01-09 05:00:17 +000038 keydefs = {
Guido van Rossum8c95c272000-02-22 00:19:58 +000039 '<<check-module>>': ['<Alt-F5>', '<Meta-F5>'],
Guido van Rossum1f3de5d1999-04-22 22:27:40 +000040 '<<import-module>>': ['<F5>'],
41 '<<run-script>>': ['<Control-F5>'],
Guido van Rossum9f425841999-01-09 05:00:17 +000042 }
Tim Peters70c43782001-01-17 08:48:39 +000043
Guido van Rossum9f425841999-01-09 05:00:17 +000044 menudefs = [
45 ('edit', [None,
Guido van Rossum8c95c272000-02-22 00:19:58 +000046 ('Check module', '<<check-module>>'),
Guido van Rossum1f3de5d1999-04-22 22:27:40 +000047 ('Import module', '<<import-module>>'),
48 ('Run script', '<<run-script>>'),
Guido van Rossum9f425841999-01-09 05:00:17 +000049 ]
50 ),
51 ]
Guido van Rossum504b0bf1999-01-02 21:28:54 +000052
53 def __init__(self, editwin):
54 self.editwin = editwin
Guido van Rossum9f425841999-01-09 05:00:17 +000055 # 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
Guido van Rossum504b0bf1999-01-02 21:28:54 +000059
Guido van Rossum8c95c272000-02-22 00:19:58 +000060 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
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000113 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
Guido van Rossumee036111999-06-01 18:18:27 +0000145 if (not sys.argv or
146 os.path.basename(sys.argv[0]) != os.path.basename(filename)):
Guido van Rossum8c95c272000-02-22 00:19:58 +0000147 # XXX Too often this discards arguments the user just set...
Guido van Rossumee036111999-06-01 18:18:27 +0000148 sys.argv = [filename]
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000149 interp.execfile(filename)
150
151 def getfilename(self):
152 # Logic to make sure we have a saved filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000153 # XXX Better logic would offer to save!
Guido van Rossum9f425841999-01-09 05:00:17 +0000154 if not self.editwin.get_saved():
Guido van Rossum36341122000-09-15 15:45:57 +0000155 name = (self.editwin.short_title() or
156 self.editwin.long_title() or
157 "Untitled")
Guido van Rossum8c95c272000-02-22 00:19:58 +0000158 self.errorbox("Not saved",
Guido van Rossum36341122000-09-15 15:45:57 +0000159 "The buffer for %s is not saved.\n" % name +
160 "Please save it first!")
Guido van Rossum9f425841999-01-09 05:00:17 +0000161 self.editwin.text.focus_set()
162 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000163 filename = self.editwin.io.filename
164 if not filename:
Guido van Rossum8c95c272000-02-22 00:19:58 +0000165 self.errorbox("No file name",
166 "This window has no file name")
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000167 return
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000168 return filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000169
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()