32 def __init__(self, source, filename, select, ignore, expected, repeat, |
34 def __init__(self, source, filename, select, ignore, expected, repeat, |
33 args): |
35 args): |
34 """ |
36 """ |
35 Constructor |
37 Constructor |
36 |
38 |
37 @param source source code to be checked (list of string) |
39 @param source source code to be checked |
38 @param filename name of the source file (string) |
40 @type list of str |
39 @param select list of selected codes (list of string) |
41 @param filename name of the source file |
40 @param ignore list of codes to be ignored (list of string) |
42 @type str |
41 @param expected list of expected codes (list of string) |
43 @param select list of selected codes |
|
44 @type list of str |
|
45 @param ignore list of codes to be ignored |
|
46 @type list of str |
|
47 @param expected list of expected codes |
|
48 @type list of str |
42 @param repeat flag indicating to report each occurrence of a code |
49 @param repeat flag indicating to report each occurrence of a code |
43 (boolean) |
50 @type bool |
44 @param args dictionary of arguments for the miscellaneous checks (dict) |
51 @param args dictionary of arguments for the miscellaneous checks |
|
52 @type dict |
45 """ |
53 """ |
46 self.__select = tuple(select) |
54 self.__select = tuple(select) |
47 self.__ignore = ('',) if select else tuple(ignore) |
55 self.__ignore = ('',) if select else tuple(ignore) |
48 self.__expected = expected[:] |
56 self.__expected = expected[:] |
49 self.__repeat = repeat |
57 self.__repeat = repeat |
54 self.__blindExceptRegex = re.compile( |
62 self.__blindExceptRegex = re.compile( |
55 r'(except:)') # __IGNORE_WARNING__ |
63 r'(except:)') # __IGNORE_WARNING__ |
56 self.__pep3101FormatRegex = re.compile( |
64 self.__pep3101FormatRegex = re.compile( |
57 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
65 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
58 |
66 |
|
67 self.__availableFutureImports = { |
|
68 # future import: (code missing, code present) |
|
69 'division': ("M701", "M721"), |
|
70 'absolute_import': ("M702", "M722"), |
|
71 'with_statement': ("M703", "M723"), |
|
72 'print_function': ("M704", "M724"), |
|
73 'unicode_literals': ("M705", "M725"), |
|
74 'generator_stop': ("M706", "M726"), |
|
75 } |
|
76 |
59 # statistics counters |
77 # statistics counters |
60 self.counters = {} |
78 self.counters = {} |
61 |
79 |
62 # collection of detected errors |
80 # collection of detected errors |
63 self.errors = [] |
81 self.errors = [] |
68 (self.__checkCopyright, ("M111", "M112")), |
86 (self.__checkCopyright, ("M111", "M112")), |
69 (self.__checkBlindExcept, ("M121",)), |
87 (self.__checkBlindExcept, ("M121",)), |
70 (self.__checkPep3101, ("M131",)), |
88 (self.__checkPep3101, ("M131",)), |
71 (self.__checkPrintStatements, ("M801",)), |
89 (self.__checkPrintStatements, ("M801",)), |
72 (self.__checkTuple, ("M811", )), |
90 (self.__checkTuple, ("M811", )), |
|
91 (self.__checkFuture, ("M701", "M702", "M703", "M704", "M705", |
|
92 "M706", "M721", "M722", "M723", "M724", |
|
93 "M725", "M726")), |
73 ] |
94 ] |
74 |
95 |
75 self.__defaultArgs = { |
96 self.__defaultArgs = { |
76 "CodingChecker": 'latin-1, utf-8', |
97 "CodingChecker": 'latin-1, utf-8', |
77 "CopyrightChecker": { |
98 "CopyrightChecker": { |
88 |
109 |
89 def __ignoreCode(self, code): |
110 def __ignoreCode(self, code): |
90 """ |
111 """ |
91 Private method to check if the message code should be ignored. |
112 Private method to check if the message code should be ignored. |
92 |
113 |
93 @param code message code to check for (string) |
114 @param code message code to check for |
94 @return flag indicating to ignore the given code (boolean) |
115 @type str |
|
116 @return flag indicating to ignore the given code |
|
117 @rtype bool |
95 """ |
118 """ |
96 return (code.startswith(self.__ignore) and |
119 return (code.startswith(self.__ignore) and |
97 not code.startswith(self.__select)) |
120 not code.startswith(self.__select)) |
98 |
121 |
99 def __error(self, lineNumber, offset, code, *args): |
122 def __error(self, lineNumber, offset, code, *args): |
100 """ |
123 """ |
101 Private method to record an issue. |
124 Private method to record an issue. |
102 |
125 |
103 @param lineNumber line number of the issue (integer) |
126 @param lineNumber line number of the issue |
104 @param offset position within line of the issue (integer) |
127 @type int |
105 @param code message code (string) |
128 @param offset position within line of the issue |
106 @param args arguments for the message (list) |
129 @type int |
|
130 @param code message code |
|
131 @type str |
|
132 @param args arguments for the message |
|
133 @type list |
107 """ |
134 """ |
108 if self.__ignoreCode(code): |
135 if self.__ignoreCode(code): |
109 return |
136 return |
110 |
137 |
111 if code in self.counters: |
138 if code in self.counters: |
262 """ |
289 """ |
263 for node in ast.walk(self.__tree): |
290 for node in ast.walk(self.__tree): |
264 if isinstance(node, ast.Tuple) and \ |
291 if isinstance(node, ast.Tuple) and \ |
265 len(node.elts) == 1: |
292 len(node.elts) == 1: |
266 self.__error(node.lineno - 1, node.col_offset, "M811") |
293 self.__error(node.lineno - 1, node.col_offset, "M811") |
|
294 |
|
295 def __checkFuture(self): |
|
296 """ |
|
297 Private method to check the __future__ imports. |
|
298 """ |
|
299 visitor = FutureImportVisitor() |
|
300 visitor.visit(self.__tree) |
|
301 if not visitor.hasCode: |
|
302 return |
|
303 |
|
304 present = set() |
|
305 for importNode in visitor.futureImports: |
|
306 for alias in importNode.names: |
|
307 if alias.name not in self.__availableFutureImports: |
|
308 # unknown code |
|
309 continue |
|
310 self.__error(importNode.lineno - 1, 0, |
|
311 self.__availableFutureImports[alias.name][1]) |
|
312 present.add(alias.name) |
|
313 for name in self.__availableFutureImports: |
|
314 if name not in present: |
|
315 self.__error(0, 0, |
|
316 self.__availableFutureImports[name][0]) |
|
317 |
|
318 |
|
319 class FutureImportVisitor(ast.NodeVisitor): |
|
320 """ |
|
321 Class implementing a node visitor to look for __future__ imports. |
|
322 """ |
|
323 def __init__(self): |
|
324 """ |
|
325 Constructor |
|
326 """ |
|
327 super(FutureImportVisitor, self).__init__() |
|
328 self.futureImports = [] |
|
329 self.__hasCode = False |
|
330 |
|
331 def visit_ImportFrom(self, node): |
|
332 """ |
|
333 Public method to analyze an 'from ... import ...' node. |
|
334 |
|
335 @param node reference to the ImportFrom node |
|
336 @type ast.AST |
|
337 """ |
|
338 if node.module == '__future__': |
|
339 self.futureImports += [node] |
|
340 |
|
341 def visit_Expr(self, node): |
|
342 """ |
|
343 Public method to analyze an expression node. |
|
344 |
|
345 @param node reference to the expression node |
|
346 @type ast.AST |
|
347 """ |
|
348 if not isinstance(node.value, ast.Str) or node.value.col_offset != 0: |
|
349 self.__hasCode = True |
|
350 |
|
351 def generic_visit(self, node): |
|
352 """ |
|
353 Public method to analyze any other node. |
|
354 |
|
355 @param node reference to the node |
|
356 @type ast.AST |
|
357 """ |
|
358 if not isinstance(node, ast.Module): |
|
359 self.__hasCode = True |
|
360 super(FutureImportVisitor, self).generic_visit(node) |
|
361 |
|
362 @property |
|
363 def hasCode(self): |
|
364 """ |
|
365 Public method to check for the presence of some code. |
|
366 """ |
|
367 return self.__hasCode or self.futureImports |