11"""Extension to execute code outside the Python shell window.
22
3- This adds two commands (to the Edit menu, until there's a separate
4- Python menu):
3+ This adds the following commands (to the Edit menu, until there's a
4+ separate Python menu):
5+
6+ - Check module (Alt-F5) does a full syntax check of the current module.
7+ It also runs the tabnanny to catch any inconsistent tabs.
58
69- Import module (F5) is equivalent to either import or reload of the
710current module. The window must have been saved previously. The
1821import imp
1922import tkMessageBox
2023
24+ indent_message = """Error: Inconsistent indentation detected!
25+
26+ This 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 \
31+ how many spaces a tab is worth.
32+
33+ To fix case 2, change all tabs to spaces by using Select All followed \
34+ by Untabify Region (both in the Edit menu)."""
2135
2236class ScriptBinding :
2337
2438 keydefs = {
39+ '<<check-module>>' : ['<Alt-F5>' , '<Meta-F5>' ],
2540 '<<import-module>>' : ['<F5>' ],
2641 '<<run-script>>' : ['<Control-F5>' ],
2742 }
2843
2944 menudefs = [
3045 ('edit' , [None ,
46+ ('Check module' , '<<check-module>>' ),
3147 ('Import module' , '<<import-module>>' ),
3248 ('Run script' , '<<run-script>>' ),
3349 ]
@@ -41,6 +57,59 @@ def __init__(self, editwin):
4157 self .flist = self .editwin .flist
4258 self .root = self .flist .root
4359
60+ 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+
44113 def import_module_event (self , event ):
45114 filename = self .getfilename ()
46115 if not filename :
@@ -75,22 +144,26 @@ def run_script_event(self, event):
75144 interp = shell .interp
76145 if (not sys .argv or
77146 os .path .basename (sys .argv [0 ]) != os .path .basename (filename )):
147+ # XXX Too often this discards arguments the user just set...
78148 sys .argv = [filename ]
79149 interp .execfile (filename )
80150
81151 def getfilename (self ):
82152 # Logic to make sure we have a saved filename
153+ # XXX Better logic would offer to save!
83154 if not self .editwin .get_saved ():
84- tkMessageBox .showerror ("Not saved" ,
85- "Please save first!" ,
86- master = self .editwin .text )
155+ self .errorbox ("Not saved" ,
156+ "Please save first!" )
87157 self .editwin .text .focus_set ()
88158 return
89159 filename = self .editwin .io .filename
90160 if not filename :
91- tkMessageBox .showerror ("No file name" ,
92- "This window has no file name" ,
93- master = self .editwin .text )
94- self .editwin .text .focus_set ()
161+ self .errorbox ("No file name" ,
162+ "This window has no file name" )
95163 return
96164 return filename
165+
166+ def errorbox (self , title , message ):
167+ # XXX This should really be a function of EditorWindow...
168+ tkMessageBox .showerror (title , message , master = self .editwin .text )
169+ self .editwin .text .focus_set ()
0 commit comments