blob: 19a20b259d4e0bd876a4a134129c3c4422363a78 [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):
Raymond Hettinger7f7d5bf2002-05-21 17:00:20 +000054 if not editwin.runnable:
55 self.menudefs = []
56 self.keydefs = {}
Guido van Rossum504b0bf1999-01-02 21:28:54 +000057 self.editwin = editwin
Guido van Rossum9f425841999-01-09 05:00:17 +000058 # Provide instance variables referenced by Debugger
59 # XXX This should be done differently
60 self.flist = self.editwin.flist
61 self.root = self.flist.root
Guido van Rossum504b0bf1999-01-02 21:28:54 +000062
Guido van Rossum8c95c272000-02-22 00:19:58 +000063 def check_module_event(self, event):
64 filename = self.getfilename()
65 if not filename:
66 return
67 if not self.tabnanny(filename):
68 return
69 if not self.checksyntax(filename):
70 return
71
72 def tabnanny(self, filename):
73 import tabnanny
74 import tokenize
Guido van Rossum8c95c272000-02-22 00:19:58 +000075 f = open(filename, 'r')
76 try:
Guido van Rossum44b1e7d2001-08-07 17:15:36 +000077 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
Guido van Rossum8c95c272000-02-22 00:19:58 +000078 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
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000115 def import_module_event(self, event):
116 filename = self.getfilename()
117 if not filename:
118 return
119
120 modname, ext = os.path.splitext(os.path.basename(filename))
121 if sys.modules.has_key(modname):
122 mod = sys.modules[modname]
123 else:
124 mod = imp.new_module(modname)
125 sys.modules[modname] = mod
126 mod.__file__ = filename
127 setattr(sys.modules['__main__'], modname, mod)
128
129 dir = os.path.dirname(filename)
130 dir = os.path.normpath(os.path.abspath(dir))
131 if dir not in sys.path:
132 sys.path.insert(0, dir)
133
134 flist = self.editwin.flist
135 shell = flist.open_shell()
136 interp = shell.interp
137 interp.runcode("reload(%s)" % modname)
138
139 def run_script_event(self, event):
140 filename = self.getfilename()
141 if not filename:
142 return
143
144 flist = self.editwin.flist
145 shell = flist.open_shell()
146 interp = shell.interp
Guido van Rossumee036111999-06-01 18:18:27 +0000147 if (not sys.argv or
148 os.path.basename(sys.argv[0]) != os.path.basename(filename)):
Guido van Rossum8c95c272000-02-22 00:19:58 +0000149 # XXX Too often this discards arguments the user just set...
Guido van Rossumee036111999-06-01 18:18:27 +0000150 sys.argv = [filename]
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000151 interp.execfile(filename)
152
153 def getfilename(self):
154 # Logic to make sure we have a saved filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000155 # XXX Better logic would offer to save!
Guido van Rossum9f425841999-01-09 05:00:17 +0000156 if not self.editwin.get_saved():
Guido van Rossum36341122000-09-15 15:45:57 +0000157 name = (self.editwin.short_title() or
158 self.editwin.long_title() or
159 "Untitled")
Guido van Rossum8c95c272000-02-22 00:19:58 +0000160 self.errorbox("Not saved",
Guido van Rossum36341122000-09-15 15:45:57 +0000161 "The buffer for %s is not saved.\n" % name +
162 "Please save it first!")
Guido van Rossum9f425841999-01-09 05:00:17 +0000163 self.editwin.text.focus_set()
164 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000165 filename = self.editwin.io.filename
166 if not filename:
Guido van Rossum8c95c272000-02-22 00:19:58 +0000167 self.errorbox("No file name",
168 "This window has no file name")
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000169 return
Guido van Rossum1f3de5d1999-04-22 22:27:40 +0000170 return filename
Guido van Rossum8c95c272000-02-22 00:19:58 +0000171
172 def errorbox(self, title, message):
173 # XXX This should really be a function of EditorWindow...
174 tkMessageBox.showerror(title, message, master=self.editwin.text)
175 self.editwin.text.focus_set()