blob: 1e05f0c0596709c3b2839ece3c989d11f7109760 [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.
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +00006 It also runs the tabnanny to catch any inconsistent tabs.
David Scherer7aced172000-08-15 01:13:23 +00007
Kurt B. Kaiser63857a42002-09-05 02:31:20 +00008- Run module executes the module's code in the __main__ namespace. The window
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +00009 must have been saved previously. The module is added to sys.modules, and is
10 also added to the __main__ namespace.
David Scherer7aced172000-08-15 01:13:23 +000011
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000012XXX GvR Redesign this interface (yet again) as follows:
Chui Tey5d2af632002-05-26 13:36:41 +000013
14- Present a dialog box for ``Run script''
15
16- Allow specify command line arguments in the dialog box
17
David Scherer7aced172000-08-15 01:13:23 +000018"""
19
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000020import re
21import string
22import tabnanny
23import tokenize
David Scherer7aced172000-08-15 01:13:23 +000024import tkMessageBox
25
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000026IDENTCHARS = string.ascii_letters + string.digits + "_"
27
David Scherer7aced172000-08-15 01:13:23 +000028indent_message = """Error: Inconsistent indentation detected!
29
30This means that either:
31
Chui Tey5d2af632002-05-26 13:36:41 +0000321) your indentation is outright incorrect (easy to fix), or
David Scherer7aced172000-08-15 01:13:23 +000033
Chui Tey5d2af632002-05-26 13:36:41 +0000342) your indentation mixes tabs and spaces in a way that depends on \
David Scherer7aced172000-08-15 01:13:23 +000035how many spaces a tab is worth.
36
37To fix case 2, change all tabs to spaces by using Select All followed \
38by Untabify Region (both in the Edit menu)."""
39
Kurt B. Kaiser969de452002-06-12 03:28:57 +000040
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000041# XXX 11Jun02 KBK TBD Implement stop-execution
42
David Scherer7aced172000-08-15 01:13:23 +000043class ScriptBinding:
Steven M. Gava42f6c642001-07-12 06:46:53 +000044
David Scherer7aced172000-08-15 01:13:23 +000045 menudefs = [
Kurt B. Kaiser969de452002-06-12 03:28:57 +000046 ('run', [None,
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000047 ('Check Module', '<<check-module>>'),
48 ('Run Script', '<<run-script>>'), ]), ]
David Scherer7aced172000-08-15 01:13:23 +000049
50 def __init__(self, editwin):
51 self.editwin = editwin
52 # Provide instance variables referenced by Debugger
53 # XXX This should be done differently
54 self.flist = self.editwin.flist
55 self.root = self.flist.root
56
57 def check_module_event(self, event):
58 filename = self.getfilename()
59 if not filename:
60 return
61 if not self.tabnanny(filename):
62 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000063 self.checksyntax(filename)
David Scherer7aced172000-08-15 01:13:23 +000064
65 def tabnanny(self, filename):
David Scherer7aced172000-08-15 01:13:23 +000066 f = open(filename, 'r')
67 try:
Kurt B. Kaiser05bab1e2002-09-18 03:05:19 +000068 tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
David Scherer7aced172000-08-15 01:13:23 +000069 except tokenize.TokenError, msg:
Neal Norwitz6453c1f2002-11-30 19:18:46 +000070 self.errorbox("Token error", "Token error:\n%s" % msg)
71 return False
David Scherer7aced172000-08-15 01:13:23 +000072 except tabnanny.NannyNag, nag:
73 # The error messages from tabnanny are too confusing...
74 self.editwin.gotoline(nag.get_lineno())
75 self.errorbox("Tab/space error", indent_message)
Neal Norwitz6453c1f2002-11-30 19:18:46 +000076 return False
77 return True
David Scherer7aced172000-08-15 01:13:23 +000078
79 def checksyntax(self, filename):
80 f = open(filename, 'r')
81 source = f.read()
82 f.close()
83 if '\r' in source:
David Scherer7aced172000-08-15 01:13:23 +000084 source = re.sub(r"\r\n", "\n", source)
85 if source and source[-1] != '\n':
86 source = source + '\n'
87 try:
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000088 # If successful, return the compiled code
89 return compile(source, filename, "exec")
David Scherer7aced172000-08-15 01:13:23 +000090 except (SyntaxError, OverflowError), err:
91 try:
92 msg, (errorfilename, lineno, offset, line) = err
93 if not errorfilename:
94 err.args = msg, (filename, lineno, offset, line)
95 err.filename = filename
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000096 self.colorize_syntax_error(msg, lineno, offset)
David Scherer7aced172000-08-15 01:13:23 +000097 except:
David Scherer7aced172000-08-15 01:13:23 +000098 msg = "*** " + str(err)
David Scherer7aced172000-08-15 01:13:23 +000099 self.errorbox("Syntax error",
100 "There's an error in your program:\n" + msg)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000101 return False
102
103 def colorize_syntax_error(self, msg, lineno, offset):
104 text = self.editwin.text
105 pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
106 text.tag_add("ERROR", pos)
107 char = text.get(pos)
108 if char and char in IDENTCHARS:
109 text.tag_add("ERROR", pos + " wordstart", pos)
110 if '\n' == text.get(pos): # error at line end
111 text.mark_set("insert", pos)
112 else:
113 text.mark_set("insert", pos + "+1c")
114 text.see(pos)
115
David Scherer7aced172000-08-15 01:13:23 +0000116 def run_script_event(self, event):
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000117 "Check syntax, if ok run the script in the shell top level"
David Scherer7aced172000-08-15 01:13:23 +0000118 filename = self.getfilename()
119 if not filename:
120 return
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000121 code = self.checksyntax(filename)
122 if not code:
123 return
David Scherer7aced172000-08-15 01:13:23 +0000124 flist = self.editwin.flist
125 shell = flist.open_shell()
126 interp = shell.interp
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000127 if interp.tkconsole.executing:
128 interp.display_executing_dialog()
129 return
130 interp.restart_subprocess()
Chui Tey5d2af632002-05-26 13:36:41 +0000131 # XXX Too often this discards arguments the user just set...
132 interp.runcommand("""if 1:
133 _filename = %s
134 import sys as _sys
135 from os.path import basename as _basename
136 if (not _sys.argv or
137 _basename(_sys.argv[0]) != _basename(_filename)):
138 _sys.argv = [_filename]
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000139 del _filename, _sys, _basename
Chui Tey5d2af632002-05-26 13:36:41 +0000140 \n""" % `filename`)
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +0000141 interp.runcode(code)
David Scherer7aced172000-08-15 01:13:23 +0000142
143 def getfilename(self):
144 # Logic to make sure we have a saved filename
145 # XXX Better logic would offer to save!
146 if not self.editwin.get_saved():
Steven M. Gava42f6c642001-07-12 06:46:53 +0000147 name = (self.editwin.short_title() or
148 self.editwin.long_title() or
149 "Untitled")
David Scherer7aced172000-08-15 01:13:23 +0000150 self.errorbox("Not saved",
Steven M. Gava42f6c642001-07-12 06:46:53 +0000151 "The buffer for %s is not saved.\n" % name +
152 "Please save it first!")
David Scherer7aced172000-08-15 01:13:23 +0000153 self.editwin.text.focus_set()
154 return
155 filename = self.editwin.io.filename
156 if not filename:
157 self.errorbox("No file name",
158 "This window has no file name")
159 return
160 return filename
161
162 def errorbox(self, title, message):
163 # XXX This should really be a function of EditorWindow...
164 tkMessageBox.showerror(title, message, master=self.editwin.text)
165 self.editwin.text.focus_set()