1 #!/usr/bin/env python2 |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # Copyright (c) 2011 - 2013 Detlev Offenbach <detlev@die-offenbachs.de> |
|
5 # |
|
6 |
|
7 """ |
|
8 Module implementing the syntax check for Python 2. |
|
9 """ |
|
10 |
|
11 import sys |
|
12 import re |
|
13 import traceback |
|
14 |
|
15 from Tools import readEncodedFile, normalizeCode, extractLineFlags |
|
16 |
|
17 |
|
18 def compile(file, codestring): |
|
19 """ |
|
20 Function to compile one Python source file to Python bytecode. |
|
21 |
|
22 @param file source filename (string) |
|
23 @param codestring source code (string) |
|
24 @return A tuple indicating status (True = an error was found), the |
|
25 file name, the line number, the index number, the code string |
|
26 and the error message (boolean, string, string, string, string, |
|
27 string). The values are only valid, if the status equals 1. |
|
28 """ |
|
29 import __builtin__ |
|
30 |
|
31 try: |
|
32 if type(file) == type(u""): |
|
33 file = file.encode('utf-8') |
|
34 |
|
35 if file.endswith('.ptl'): |
|
36 try: |
|
37 import quixote.ptl_compile |
|
38 except ImportError: |
|
39 return (0, None, None, None, None) |
|
40 template = quixote.ptl_compile.Template(codestring, file) |
|
41 template.compile() |
|
42 else: |
|
43 __builtin__.compile(codestring, file, 'exec') |
|
44 except SyntaxError, detail: |
|
45 index = "0" |
|
46 code = "" |
|
47 error = "" |
|
48 lines = traceback.format_exception_only(SyntaxError, detail) |
|
49 match = re.match('\s*File "(.+)", line (\d+)', |
|
50 lines[0].replace('<string>', '%s' % file)) |
|
51 if match is not None: |
|
52 fn, line = match.group(1, 2) |
|
53 if lines[1].startswith('SyntaxError:'): |
|
54 error = re.match('SyntaxError: (.+)', lines[1]).group(1) |
|
55 else: |
|
56 code = re.match('(.+)', lines[1]).group(1) |
|
57 for seLine in lines[2:]: |
|
58 if seLine.startswith('SyntaxError:'): |
|
59 error = re.match('SyntaxError: (.+)', seLine).group(1) |
|
60 elif seLine.rstrip().endswith('^'): |
|
61 index = len(seLine.rstrip()) - 4 |
|
62 else: |
|
63 fn = detail.filename |
|
64 line = detail.lineno and detail.lineno or 1 |
|
65 error = detail.msg |
|
66 return (1, fn, line, index, code, error) |
|
67 except ValueError, detail: |
|
68 index = "0" |
|
69 code = "" |
|
70 try: |
|
71 fn = detail.filename |
|
72 line = detail.lineno |
|
73 error = detail.msg |
|
74 except AttributeError: |
|
75 fn = file |
|
76 line = 1 |
|
77 error = unicode(detail) |
|
78 return (1, fn, line, index, code, error) |
|
79 except StandardError, detail: |
|
80 try: |
|
81 fn = detail.filename |
|
82 line = detail.lineno |
|
83 index = "0" |
|
84 code = "" |
|
85 error = detail.msg |
|
86 return (1, fn, line, index, code, error) |
|
87 except: # this catchall is intentional |
|
88 pass |
|
89 |
|
90 return (0, None, None, None, None, None) |
|
91 |
|
92 |
|
93 def flakesCheck(fileName, codestring, ignoreStarImportWarnings): |
|
94 """ |
|
95 Function to perform a pyflakes check. |
|
96 |
|
97 @param fileName name of the file (string) |
|
98 @param codestring source code to be checked (string) |
|
99 @param ignoreStarImportWarnings flag indicating to |
|
100 ignore 'star import' warnings (boolean) |
|
101 @return list of strings containing the warnings |
|
102 (marker, file name, line number, message) |
|
103 """ |
|
104 from py2flakes.checker import Checker |
|
105 from py2flakes.messages import ImportStarUsed |
|
106 |
|
107 strings = [] |
|
108 lines = codestring.splitlines() |
|
109 try: |
|
110 warnings = Checker(codestring, fileName) |
|
111 warnings.messages.sort(key=lambda a: a.lineno) |
|
112 for warning in warnings.messages: |
|
113 if ignoreStarImportWarnings and \ |
|
114 isinstance(warning, ImportStarUsed): |
|
115 continue |
|
116 |
|
117 _fn, lineno, message = warning.getMessageData() |
|
118 if "__IGNORE_WARNING__" not in extractLineFlags(lines[lineno - 1].strip()): |
|
119 strings.extend(["FLAKES_WARNING", _fn, lineno, message]) |
|
120 except SyntaxError as err: |
|
121 if err.text.strip(): |
|
122 msg = err.text.strip() |
|
123 else: |
|
124 msg = err.msg |
|
125 strings.extend(["FLAKES_ERROR", fileName, err.lineno, msg]) |
|
126 |
|
127 return strings |
|
128 |
|
129 if __name__ == "__main__": |
|
130 if len(sys.argv) < 2 or \ |
|
131 len(sys.argv) > 3 or \ |
|
132 (len(sys.argv) == 3 and sys.argv[1] not in ["-fi", "-fs"]): |
|
133 print "ERROR" |
|
134 print "" |
|
135 print "" |
|
136 print "" |
|
137 print "" |
|
138 print "No file name given." |
|
139 else: |
|
140 filename = sys.argv[-1] |
|
141 try: |
|
142 codestring = readEncodedFile(filename)[0] |
|
143 codestring = normalizeCode(codestring) |
|
144 |
|
145 syntaxerror, fname, line, index, code, error = \ |
|
146 compile(filename, codestring) |
|
147 except IOError, msg: |
|
148 # fake a syntax error |
|
149 syntaxerror, fname, line, index, code, error = \ |
|
150 1, filename, "1", "0", "", "I/O Error: %s" % unicode(msg) |
|
151 |
|
152 if syntaxerror: |
|
153 print "ERROR" |
|
154 else: |
|
155 print "NO_ERROR" |
|
156 print fname |
|
157 print line |
|
158 print index |
|
159 print code |
|
160 print error |
|
161 |
|
162 if not syntaxerror and sys.argv[1] in ["-fi", "-fs"]: |
|
163 # do pyflakes check |
|
164 warningLines = flakesCheck(filename, codestring, sys.argv[1] == "-fi") |
|
165 for warningLine in warningLines: |
|
166 print warningLine |
|
167 |
|
168 sys.exit(0) |
|
169 |
|
170 # |
|
171 # eflag: FileType = Python2 |
|