blob: 4071681b7fd16864b7bcb1ae1523c81f53981233 [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
Guido van Rossum8c95c272000-02-22 00:19:58 +000072 f = open(filename, 'r')
73 try:
Guido van Rossum44b1e7d2001-08-07 17:15:36 +000074 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
Guido van Rossum8c95c272000-02-22 00:19:58 +000075 except tokenize.TokenError, msg:
76 self.errorbox("Token error",
77 "Token error:\n%s" % str(msg))
78 return 0
79 except tabnanny.NannyNag, nag:
80 # The error messages from tabnanny are too confusing...
81 self.editwin.gotoline(nag.get_lineno())
82 self.errorbox("Tab/space error", indent_message)
83 return 0
84 return 1
85
86 def checksyntax(self, filename):
87 f = open(filename, 'r')
88 source = f.read()
89 f.close()
90 if '\r' in source:
91 import re
92 source = re.sub(r"\r\n", "\n", source)
93 if source and source[-1] != '\n':
94 source = source + '\n'
95 try:
96 compile(source, filename, "exec")
97 except (SyntaxError, OverflowError), err:
98 try:
99 msg, (errorfilename, lineno, offset, line) = err
100 if not errorfilename:
101 err.args = msg, (filename, lineno, offset, line)
102 err.filename = filename
103 except:
104 lineno = None
105 msg = "*** " + str(err)
106 if lineno:
107 self.editwin.gotoline(lineno)
108 self.errorbox("Syntax error",
109 "There's an error in your program:\n" + msg)
110 return 1
111
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000112 def import_module_event(self, event):
113 filename = self.getfilename()
114 if not filename:
115 return
116
117 modname, ext = os.path.splitext(os.path.basename(filename))
118 if sys.modules.has_key(modname):
119 mod = sys.modules[modname]
120 else:
121 mod = imp.new_module(modname)
122 sys.modules[modname] = mod
123 mod.__file__ = filename
124 setattr(sys.modules['__main__'], modname, mod)
125
126 dir = os.path.dirname(filename)
127 dir = os.path.normpath(os.path.abspath(dir))
128 if dir not in sys.path:
129 sys.path.insert(0, dir)
130
131 flist = self.editwin.flist
132 shell = flist.open_shell()
133 interp = shell.interp
134 interp.runcode("reload(%s)" % modname)
135
136 def run_script_event(self, event):
137 filename = self.getfilename()
138 if not filename:
139 return
140
141 flist = self.editwin.flist
142 shell = flist.open_shell()
143 interp = shell.interp
Guido van Rossumee036111999-06-01 18:18:27 +0000144 if (not sys.argv or
145 os.path.basename(sys.argv[0]) != os.path.basename(filename)):
Guido van Rossum8c95c272000-02-22 00:19:58 +0000146 # XXX Too often this discards arguments the user just set...
Guido van Rossumee036111999-06-01 18:18:27 +0000147 sys.argv = [filename]
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000148 interp.execfile(filename)
149
150 def getfilename(self):
151 # Logic to make sure we have a saved filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000152 # XXX Better logic would offer to save!
Guido van Rossum9f425841999-01-09 05:00:17 +0000153 if not self.editwin.get_saved():
Guido van Rossum36341122000-09-15 15:45:57 +0000154 name = (self.editwin.short_title() or
155 self.editwin.long_title() or
156 "Untitled")
Guido van Rossum8c95c272000-02-22 00:19:58 +0000157 self.errorbox("Not saved",
Guido van Rossum36341122000-09-15 15:45:57 +0000158 "The buffer for %s is not saved.\n" % name +
159 "Please save it first!")
Guido van Rossum9f425841999-01-09 05:00:17 +0000160 self.editwin.text.focus_set()
161 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000162 filename = self.editwin.io.filename
163 if not filename:
Guido van Rossum8c95c272000-02-22 00:19:58 +0000164 self.errorbox("No file name",
165 "This window has no file name")
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000166 return
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000167 return filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000168
169 def errorbox(self, title, message):
170 # XXX This should really be a function of EditorWindow...
171 tkMessageBox.showerror(title, message, master=self.editwin.text)
172 self.editwin.text.focus_set()