blob: 6d4c652c5e4495a354ab6d41cdccc283e4a1cf1c [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001"""Extension to execute code outside the Python shell window.
2
Kurt B. Kaiser969de452002-06-12 03:28:57 +00003This adds the following commands:
David Scherer7aced172000-08-15 01:13:23 +00004
Kurt B. Kaiser969de452002-06-12 03:28:57 +00005- Check module does a full syntax check of the current module.
David Scherer7aced172000-08-15 01:13:23 +00006It also runs the tabnanny to catch any inconsistent tabs.
7
Kurt B. Kaiser969de452002-06-12 03:28:57 +00008- Import module is equivalent to either import or reload of the
David Scherer7aced172000-08-15 01:13:23 +00009current module. The window must have been saved previously. The
10module is added to sys.modules, and is also added to the __main__
11namespace. Output goes to the shell window.
12
Kurt B. Kaiser969de452002-06-12 03:28:57 +000013- Run module does the same but executes the module's
David Scherer7aced172000-08-15 01:13:23 +000014code in the __main__ namespace.
15
Chui Tey5d2af632002-05-26 13:36:41 +000016XXX Redesign this interface (yet again) as follows:
17
18- Present a dialog box for ``Run script''
19
20- Allow specify command line arguments in the dialog box
21
22- Restart the interpreter when running a script
23
David Scherer7aced172000-08-15 01:13:23 +000024"""
25
26import sys
27import os
28import imp
29import tkMessageBox
30
31indent_message = """Error: Inconsistent indentation detected!
32
33This means that either:
34
Chui Tey5d2af632002-05-26 13:36:41 +0000351) your indentation is outright incorrect (easy to fix), or
David Scherer7aced172000-08-15 01:13:23 +000036
Chui Tey5d2af632002-05-26 13:36:41 +0000372) your indentation mixes tabs and spaces in a way that depends on \
David Scherer7aced172000-08-15 01:13:23 +000038how many spaces a tab is worth.
39
40To fix case 2, change all tabs to spaces by using Select All followed \
41by Untabify Region (both in the Edit menu)."""
42
Kurt B. Kaiser969de452002-06-12 03:28:57 +000043
44# XXX TBD Implement stop-execution KBK 11Jun02
David Scherer7aced172000-08-15 01:13:23 +000045class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000046
David Scherer7aced172000-08-15 01:13:23 +000047 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000048 ('run', [None,
49# ('Check module', '<<check-module>>'),
50# ('Import module', '<<import-module>>'),
David Scherer7aced172000-08-15 01:13:23 +000051 ('Run script', '<<run-script>>'),
52 ]
53 ),
54 ]
55
56 def __init__(self, editwin):
57 self.editwin = editwin
58 # 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
62
63 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
75 tabnanny.reset_globals()
76 f = open(filename, 'r')
77 try:
78 tokenize.tokenize(f.readline, tabnanny.tokeneater)
79 except tokenize.TokenError, msg:
80 self.errorbox("Token error",
81 "Token error:\n%s" % str(msg))
82 return 0
83 except tabnanny.NannyNag, nag:
84 # The error messages from tabnanny are too confusing...
85 self.editwin.gotoline(nag.get_lineno())
86 self.errorbox("Tab/space error", indent_message)
87 return 0
88 return 1
89
90 def checksyntax(self, filename):
91 f = open(filename, 'r')
92 source = f.read()
93 f.close()
94 if '\r' in source:
95 import re
96 source = re.sub(r"\r\n", "\n", source)
97 if source and source[-1] != '\n':
98 source = source + '\n'
99 try:
100 compile(source, filename, "exec")
101 except (SyntaxError, OverflowError), err:
102 try:
103 msg, (errorfilename, lineno, offset, line) = err
104 if not errorfilename:
105 err.args = msg, (filename, lineno, offset, line)
106 err.filename = filename
107 except:
108 lineno = None
109 msg = "*** " + str(err)
110 if lineno:
111 self.editwin.gotoline(lineno)
112 self.errorbox("Syntax error",
113 "There's an error in your program:\n" + msg)
114 return 1
115
116 def import_module_event(self, event):
Chui Tey5d2af632002-05-26 13:36:41 +0000117 flist = self.editwin.flist
118 shell = flist.open_shell()
119 interp = shell.interp
120
David Scherer7aced172000-08-15 01:13:23 +0000121 filename = self.getfilename()
122 if not filename:
123 return
124
125 modname, ext = os.path.splitext(os.path.basename(filename))
David Scherer7aced172000-08-15 01:13:23 +0000126
127 dir = os.path.dirname(filename)
128 dir = os.path.normpath(os.path.abspath(dir))
David Scherer7aced172000-08-15 01:13:23 +0000129
Chui Tey5d2af632002-05-26 13:36:41 +0000130 interp.runcode("""if 1:
131 import sys as _sys
132 if %s not in _sys.path:
133 _sys.path.insert(0, %s)
134 if _sys.modules.get(%s):
135 del _sys
136 import %s
137 reload(%s)
138 else:
139 del _sys
140 import %s
141 \n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
David Scherer7aced172000-08-15 01:13:23 +0000142
143 def run_script_event(self, event):
144 filename = self.getfilename()
145 if not filename:
146 return
David Scherer7aced172000-08-15 01:13:23 +0000147 flist = self.editwin.flist
148 shell = flist.open_shell()
149 interp = shell.interp
Kurt B. Kaiserd694c1f2002-07-28 03:35:31 +0000150 # clear the subprocess environment before every Run/F5 invocation
151 interp.rpcclt.remotecall("exec", "clear_the_environment", (), {})
Chui Tey5d2af632002-05-26 13:36:41 +0000152 # XXX Too often this discards arguments the user just set...
153 interp.runcommand("""if 1:
154 _filename = %s
155 import sys as _sys
156 from os.path import basename as _basename
157 if (not _sys.argv or
158 _basename(_sys.argv[0]) != _basename(_filename)):
Kurt B. Kaiserd694c1f2002-07-28 03:35:31 +0000159 # XXX 25 July 2002 KBK should this be sys.argv not _sys.argv?
Chui Tey5d2af632002-05-26 13:36:41 +0000160 _sys.argv = [_filename]
161 del _filename, _sys, _basename
162 \n""" % `filename`)
David Scherer7aced172000-08-15 01:13:23 +0000163 interp.execfile(filename)
164
165 def getfilename(self):
166 # Logic to make sure we have a saved filename
167 # XXX Better logic would offer to save!
168 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000169 name = (self.editwin.short_title() or
170 self.editwin.long_title() or
171 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000172 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000173 "The buffer for %s is not saved.\n" % name +
174 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000175 self.editwin.text.focus_set()
176 return
177 filename = self.editwin.io.filename
178 if not filename:
179 self.errorbox("No file name",
180 "This window has no file name")
181 return
182 return filename
183
184 def errorbox(self, title, message):
185 # XXX This should really be a function of EditorWindow...
186 tkMessageBox.showerror(title, message, master=self.editwin.text)
187 self.editwin.text.focus_set()