Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py

changeset 4508
a3b38825acf0
parent 4507
1a5bc1ac7c2e
child 4509
7797ee4a45f9
equal deleted inserted replaced
4507:1a5bc1ac7c2e 4508:a3b38825acf0
21 Codes = [ 21 Codes = [
22 "M101", "M102", 22 "M101", "M102",
23 "M111", "M112", 23 "M111", "M112",
24 "M121", 24 "M121",
25 "M131", 25 "M131",
26 "M701", "M702", "M703", "M704", "M705", "M706",
27 "M721", "M722", "M723", "M724", "M725", "M726",
26 "M801", 28 "M801",
27 "M811", 29 "M811",
28 30
29 "M901", 31 "M901",
30 ] 32 ]
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

eric ide

mercurial