UtilitiesPython2/Py2SyntaxChecker.py

changeset 795
917f1945355c
child 802
e8882d16384c
equal deleted inserted replaced
794:e338e1e8f3ca 795:917f1945355c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the syntax check for Python 2.
8 """
9
10 import sys
11 import re
12 import traceback
13 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32
14
15 coding_regexps = [
16 (2, re.compile(r'''coding[:=]\s*([-\w_.]+)''')),
17 (1, re.compile(r'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')),
18 ]
19
20 def get_coding(text):
21 """
22 Function to get the coding of a text.
23
24 @param text text to inspect (string)
25 @return coding string
26 """
27 lines = text.splitlines()
28 for coding in coding_regexps:
29 coding_re = coding[1]
30 head = lines[:coding[0]]
31 for l in head:
32 m = coding_re.search(l)
33 if m:
34 return m.group(1).lower()
35 return None
36
37 def decode(text):
38 """
39 Function to decode a text.
40
41 @param text text to decode (string)
42 @return decoded text and encoding
43 """
44 try:
45 if text.startswith(BOM_UTF8):
46 # UTF-8 with BOM
47 return unicode(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom'
48 elif text.startswith(BOM_UTF16):
49 # UTF-16 with BOM
50 return unicode(text[len(BOM_UTF16):], 'utf-16'), 'utf-16'
51 elif text.startswith(BOM_UTF32):
52 # UTF-32 with BOM
53 return unicode(text[len(BOM_UTF32):], 'utf-32'), 'utf-32'
54 coding = get_coding(text)
55 if coding:
56 return unicode(text, coding), coding
57 except (UnicodeError, LookupError):
58 pass
59
60 # Assume UTF-8
61 try:
62 return unicode(text, 'utf-8'), 'utf-8-guessed'
63 except (UnicodeError, LookupError):
64 pass
65
66 # Assume Latin-1 (behaviour before 3.7.1)
67 return unicode(text, "latin-1"), 'latin-1-guessed'
68
69 def compile(file):
70 """
71 Function to compile one Python source file to Python bytecode.
72
73 @param file source filename (string)
74 @return A tuple indicating status (1 = an error was found), the
75 filename, the linenumber, the code string and the error message
76 (boolean, string, string, string, string). The values are only
77 valid, if the status equals 1.
78 """
79 import __builtin__
80 try:
81 f = open(file)
82 codestring, encoding = decode(f.read())
83 f.close()
84 except IOError, msg:
85 return (1, file, "1", "", "I/O Error: %s" % unicode(msg))
86
87 if type(codestring) == type(u""):
88 codestring = codestring.encode('utf-8')
89 codestring = codestring.replace("\r\n","\n")
90 codestring = codestring.replace("\r","\n")
91
92 if codestring and codestring[-1] != '\n':
93 codestring = codestring + '\n'
94
95 try:
96 if type(file) == type(u""):
97 file = file.encode('utf-8')
98
99 if file.endswith('.ptl'):
100 try:
101 import quixote.ptl_compile
102 except ImportError:
103 return (0, None, None, None, None)
104 template = quixote.ptl_compile.Template(codestring, file)
105 template.compile()
106 codeobject = template.code
107 else:
108 codeobject = __builtin__.compile(codestring, file, 'exec')
109 except SyntaxError, detail:
110 lines = traceback.format_exception_only(SyntaxError, detail)
111 match = re.match('\s*File "(.+)", line (\d+)',
112 lines[0].replace('<string>', '%s' % file))
113 if match is not None:
114 fn, line = match.group(1, 2)
115 if lines[1].startswith('SyntaxError:'):
116 code = ""
117 error = re.match('SyntaxError: (.+)', lines[1]).group(1)
118 else:
119 code = re.match('(.+)', lines[1]).group(1)
120 error = ""
121 for seLine in lines[2:]:
122 if seLine.startswith('SyntaxError:'):
123 error = re.match('SyntaxError: (.+)', seLine).group(1)
124 else:
125 fn = detail.filename
126 line = detail.lineno and detail.lineno or 1
127 code = ""
128 error = detail.msg
129 return (1, fn, line, code, error)
130 except ValueError, detail:
131 try:
132 fn = detail.filename
133 line = detail.lineno
134 error = detail.msg
135 except AttributeError:
136 fn = file
137 line = 1
138 error = unicode(detail)
139 code = ""
140 return (1, fn, line, code, error)
141 except StandardError, detail:
142 try:
143 fn = detail.filename
144 line = detail.lineno
145 code = ""
146 error = detail.msg
147 return (1, fn, line, code, error)
148 except: # this catchall is intentional
149 pass
150
151 return (0, None, None, None, None)
152
153 if __name__ == "__main__":
154 if len(sys.argv) != 2:
155 print "ERROR"
156 print ""
157 print ""
158 print ""
159 print "No file name given."
160 else:
161 filename = sys.argv[1]
162 res, fname, line, code, error = compile(filename)
163
164 if res:
165 print "ERROR"
166 else:
167 print "NO_ERROR"
168 print fname
169 print line
170 print code
171 print error
172
173 sys.exit(0)
174
175 #
176 # eflag: FileType = Python2

eric ide

mercurial